perm filename LRNSAM.DGL[UP,DOC]1 blob sn#279768 filedate 1977-04-30 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00111 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00018 00002
C00020 00003
C00022 00004	LRNSAM                                                                     DRAFT
C00026 00005	LRNSAM                         Table of Contents                           DRAFT
C00031 00006	LRNSAM                         Table of Contents                           DRAFT
C00037 00007	LRNSAM                         Table of Contents                           DRAFT
C00041 00008	LRNSAM                                                                     DRAFT
C00045 00009	LRNSAM                                                                     DRAFT
C00049 00010	LRNSAM                                                                     DRAFT
C00054 00011	LRNSAM                 Processing ticks and update ticks                   DRAFT
C00058 00012	LRNSAM                                                                     DRAFT
C00062 00013	LRNSAM                       Tick time requirements                        DRAFT
C00064 00014	LRNSAM                                                                     DRAFT
C00069 00015	LRNSAM                   Processing speed vs. capacity                     DRAFT
C00074 00016	LRNSAM                   Processing speed vs. capacity                     DRAFT
C00077 00017	LRNSAM                                                                     DRAFT
C00081 00018	LRNSAM                      The processing elements                        DRAFT
C00083 00019	LRNSAM                                                                     DRAFT
C00084 00020	LRNSAM                                                                     DRAFT
C00088 00021	LRNSAM                          The flow of data                           DRAFT
C00092 00022	LRNSAM                          The flow of data                           DRAFT
C00095 00023	LRNSAM                                                                     DRAFT
C00100 00024	LRNSAM                             Sum memory                              DRAFT
C00105 00025	LRNSAM                             Sum memory                              DRAFT
C00107 00026	LRNSAM                                                                     DRAFT
C00112 00027	LRNSAM                             Generators                              DRAFT
C00116 00028	LRNSAM                             Generators                              DRAFT
C00119 00029	LRNSAM                                                                     DRAFT
C00123 00030	LRNSAM                        Generator run modes                          DRAFT
C00128 00031	LRNSAM                        Generator run modes                          DRAFT
C00133 00032	LRNSAM                        Generator run modes                          DRAFT
C00134 00033	LRNSAM                                                                     DRAFT
C00135 00034	LRNSAM                                                                     DRAFT
C00141 00035	LRNSAM                       Oscillator processing                         DRAFT
C00143 00036	LRNSAM                                                                     DRAFT
C00147 00037	LRNSAM                          Oscillator modes                           DRAFT
C00151 00038	LRNSAM                          Oscillator modes                           DRAFT
C00154 00039	LRNSAM                                                                     DRAFT
C00159 00040	LRNSAM                        Envelope processing                          DRAFT
C00162 00041	LRNSAM                                                                     DRAFT
C00166 00042	LRNSAM                           Envelope modes                            DRAFT
C00168 00043	LRNSAM                                                                     DRAFT
C00172 00044	LRNSAM                     Shape-shifting generators                       DRAFT
C00177 00045	LRNSAM                     Shape-shifting generators                       DRAFT
C00181 00046	LRNSAM                                                                     DRAFT
C00186 00047	LRNSAM                           Magic numbers                             DRAFT
C00192 00048	LRNSAM                           Magic numbers                             DRAFT
C00195 00049	LRNSAM                                                                     DRAFT
C00199 00050	LRNSAM                             Modifiers                               DRAFT
C00204 00051	LRNSAM                             Modifiers                               DRAFT
C00206 00052	LRNSAM                                                                     DRAFT
C00210 00053	LRNSAM                        Modifier procedures                          DRAFT
C00214 00054	LRNSAM                        Modifier procedures                          DRAFT
C00217 00055	LRNSAM                        Modifier procedures                          DRAFT
C00221 00056	LRNSAM                        Modifier procedures                          DRAFT
C00226 00057	LRNSAM                        Modifier procedures                          DRAFT
C00230 00058	LRNSAM                        Modifier procedures                          DRAFT
C00231 00059	LRNSAM                                                                     DRAFT
C00234 00060	LRNSAM                        Filtering algorithms                         DRAFT
C00236 00061	LRNSAM                        Filtering algorithms                         DRAFT
C00240 00062	LRNSAM                        Filtering algorithms                         DRAFT
C00245 00063	LRNSAM                        Filtering algorithms                         DRAFT
C00250 00064	LRNSAM                        Filtering algorithms                         DRAFT
C00251 00065	LRNSAM                                                                     DRAFT
C00255 00066	LRNSAM                            Delay Units                              DRAFT
C00257 00067	LRNSAM                                                                     DRAFT
C00261 00068	LRNSAM                          Calling the box                            DRAFT
C00265 00069	LRNSAM                          Calling the box                            DRAFT
C00269 00070	LRNSAM                          Calling the box                            DRAFT
C00272 00071	LRNSAM                          Calling the box                            DRAFT
C00276 00072	LRNSAM                          Calling the box                            DRAFT
C00280 00073	LRNSAM                          Calling the box                            DRAFT
C00283 00074	LRNSAM                          Calling the box                            DRAFT
C00287 00075	LRNSAM                          Calling the box                            DRAFT
C00291 00076	LRNSAM                                                                     DRAFT
C00296 00077	LRNSAM                   An introduction to pipelining                     DRAFT
C00302 00078	LRNSAM                                                                     DRAFT
C00306 00079	LRNSAM                     Time division multiplexing                      DRAFT
C00310 00080	LRNSAM                                                                     DRAFT
C00314 00081	LRNSAM                        Basic number theory                          DRAFT
C00318 00082	LRNSAM                        Basic number theory                          DRAFT
C00321 00083	LRNSAM                        Basic number theory                          DRAFT
C00324 00084	LRNSAM                                                                     DRAFT
C00328 00085	LRNSAM                           SAIL examples                             DRAFT
C00332 00086	LRNSAM                           SAIL examples                             DRAFT
C00335 00087	LRNSAM                           SAIL examples                             DRAFT
C00340 00088	LRNSAM                           SAIL examples                             DRAFT
C00342 00089	LRNSAM                           SAIL examples                             DRAFT
C00347 00090	LRNSAM                           SAIL examples                             DRAFT
C00350 00091	LRNSAM                           SAIL examples                             DRAFT
C00353 00092	LRNSAM                           SAIL examples                             DRAFT
C00357 00093	LRNSAM                           SAIL examples                             DRAFT
C00360 00094	LRNSAM                           SAIL examples                             DRAFT
C00362 00095	LRNSAM                                                                     DRAFT
C00365 00096	LRNSAM                         Delay line example                          DRAFT
C00369 00097	LRNSAM                         Delay line example                          DRAFT
C00371 00098	LRNSAM                                                                     DRAFT
C00375 00099	LRNSAM                  Generator field and mode tables                    DRAFT
C00377 00100	LRNSAM                                                                     DRAFT
C00380 00101	LRNSAM                   Modifier field and mode tables                    DRAFT
C00383 00102	LRNSAM                                                                     DRAFT
C00386 00103	LRNSAM                                                                     DRAFT
C00390 00104	LRNSAM                           Reserved Words                            DRAFT
C00394 00105	LRNSAM                           Reserved Words                            DRAFT
C00398 00106	LRNSAM                           Reserved Words                            DRAFT
C00401 00107	LRNSAM                                                                     DRAFT
C00403 00108	LRNSAM                               Index                                 DRAFT
C00409 00109	LRNSAM                               Index                                 DRAFT
C00415 00110	LRNSAM                               Index                                 DRAFT
C00421 00111	
C00422 ENDMK
C⊗;












                                Systems Concepts
                              Digital Synthesizer
                               operations manual
                                  and tutorial



                                 by Gareth Loy
              Center for Computer Research in Music and Acoustics
                              Department of Music
                              Stanford University
                           Stanford, California 94305











This work was supported in part  by a grant from the National Endowment  for the
Arts and by NSF contract DCR 75-00694.



Abstract


This  document  describes  the  low level  operation  of  Pete  Samson's Systems
Concepts  Digital  Synthesizer,  beginning  with  a  general  discussion  of the
internal  and external  data  structures, then  the workings  of  the processing
elements, finally,  a discussion of  the SAIL procedures  which form  the lowest
level of user  control.  At the end  are appendices containing  introductions to
pipelining and  time-division multiplexing, and  examples of sample  calls using
the SAIL procedures.


Acknowledgments


The existence  of this document  is entirely due  to the patience,  teaching and
editing I have received over the course of its writing from Andy Moorer and Mark
Kahrs.
LRNSAM                                                                     DRAFT


                       T A B L E   O F   C O N T E N T S



                                    SECTION                                 PAGE



Section 1   Introduction
             1-1     The nature of the problem  .  .  .  .  .  .  .  .  .  .   1
             1-2     Reading this manual  .  .  .  .  .  .  .  .  .  .  .  .   1

Section 2   Definitions

Section 3   Processing ticks and update ticks

Section 4   Tick time requirements
             4-1     Generator tick time  .  .  .  .  .  .  .  .  .  .  .  .   5
             4-2     Modifier tick time   .  .  .  .  .  .  .  .  .  .  .  .   5
             4-3     Combined tick times  .  .  .  .  .  .  .  .  .  .  .  .   5
             4-4     General formula   .  .  .  .  .  .  .  .  .  .  .  .  .   6

Section 5   Processing speed vs. capacity
             5-1     Speed of processing  .  .  .  .  .  .  .  .  .  .  .  .   7
             5-2     Ticks per pass .  .  .  .  .  .  .  .  .  .  .  .  .  .   7
             5-3     Processing vs. update ticks   .  .  .  .  .  .  .  .  .   8
             5-4     Budgeting ticks   .  .  .  .  .  .  .  .  .  .  .  .  .   8
             5-5     Numbering Processing elements .  .  .  .  .  .  .  .  .   9

Section 6   The processing elements
             6-1     Generators  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  10
             6-2     Modifiers   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  10
             6-3     Sum memory  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  11
             6-4     Delay memory   .  .  .  .  .  .  .  .  .  .  .  .  .  .  11

Section 7   The flow of data
             7-1     The Connection... .  .  .  .  .  .  .  .  .  .  .  .  .  13
             7-2     Command path   .  .  .  .  .  .  .  .  .  .  .  .  .  .  13
             7-3     I/O path .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  14
             7-4     Read path and Write path   .  .  .  .  .  .  .  .  .  .  14
             7-5     DAC path .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  14
             7-6     It's a Stream machine...   .  .  .  .  .  .  .  .  .  .  14

Section 8   Sum memory
             8-1     This pass - last pass division   .  .  .  .  .  .  .  .  16
             8-2     Generator - modifier division .  .  .  .  .  .  .  .  .  17
             8-3     Addressing sum memory   .  .  .  .  .  .  .  .  .  .  .  17


                                     Page i
LRNSAM                         Table of Contents                           DRAFT


Section 9   Generators
             9-1     Generator parameters .  .  .  .  .  .  .  .  .  .  .  .  20

Section 10  Generator run modes
             10-1    INACTIVE .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  22
             10-2    G_PAUSE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  22
             10-3    G_WAIT   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  23
             10-4    x_RUNNING   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  23
             10-5    DAC_WRITE   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  24
             10-6    READ_DATA   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  24
             10-7    WRITE_DATA  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  24

Section 11  Oscillator processing

Section 12  Oscillator modes
             12-1    COSINE   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  29
             12-2    SAWTOOTH .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  29
             12-3    SQUARE   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  30
             12-4    PULSE_TRAIN .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  30
             12-5    COS_FM   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  30
             12-6    SUM_OF_COSINES .  .  .  .  .  .  .  .  .  .  .  .  .  .  30
             12-7    A note on band limiting .  .  .  .  .  .  .  .  .  .  .  31

Section 13  Envelope processing
             13-1    EXPONENT and RATE .  .  .  .  .  .  .  .  .  .  .  .  .  32
             13-2    ASYMPTOTE   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  32

Section 14  Envelope modes

Section 15  Shape-shifting generators
             15-1    RATE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  36
             15-2    FREQUENCY   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  37
             15-3    ANGLE .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  37
             15-4    NCOSINES and SCALE   .  .  .  .  .  .  .  .  .  .  .  .  37
             15-5    FM .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  38
             15-6    SUM_MEMORY  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  38
             15-7    RATE EXPONENT and ASYMPTOTE   .  .  .  .  .  .  .  .  .  38

Section 16  Magic numbers

Section 17  Modifiers
             17-1    Modifier parameters  .  .  .  .  .  .  .  .  .  .  .  .  42
             17-2    Running terms  .  .  .  .  .  .  .  .  .  .  .  .  .  .  42
             17-3    Coefficient terms and Read terms .  .  .  .  .  .  .  .  43
             17-4    Scaling terms  .  .  .  .  .  .  .  .  .  .  .  .  .  .  43
             17-5    Mode  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  43
             17-6    Sum memory  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  44
             17-7    Initializing   .  .  .  .  .  .  .  .  .  .  .  .  .  .  44

                                    Page ii
LRNSAM                         Table of Contents                           DRAFT


Section 18  Modifier procedures
             18-1    Inactive .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  45
             18-2    Mixing   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  45
             18-3    Integer mixing .  .  .  .  .  .  .  .  .  .  .  .  .  .  46
             18-4    Latch .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  46
             18-5    Signum   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  47
             18-6    Zero-crossing pulser .  .  .  .  .  .  .  .  .  .  .  .  47
             18-7    Minimum  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  47
             18-8    Maximum  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  47
             18-9    Amplitude modulation .  .  .  .  .  .  .  .  .  .  .  .  48
             18-10   Four-quadrant-multiply  .  .  .  .  .  .  .  .  .  .  .  48
             18-11   Uniform noise  .  .  .  .  .  .  .  .  .  .  .  .  .  .  48
             18-12   Triggered uniform noise .  .  .  .  .  .  .  .  .  .  .  49
             18-13   Threshold   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  50
             18-14   Invoke delay unit .  .  .  .  .  .  .  .  .  .  .  .  .  50

Section 19  Filtering algorithms
             19-1    One pole .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  52
             19-2    One zero .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  52
             19-3    Two poles   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  53
             19-4    Two zeros   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  53
             19-5    Two poles COEFF0 variable  .  .  .  .  .  .  .  .  .  .  53
             19-6    Two poles COEFF1 variable  .  .  .  .  .  .  .  .  .  .  53
             19-7    Two zeros COEFF0 variable  .  .  .  .  .  .  .  .  .  .  54
             19-8    Two zeros COEFF1 variable  .  .  .  .  .  .  .  .  .  .  54
             19-9    A little digital filtering theory   .  .  .  .  .  .  .  54

Section 20  Delay Units
             20-1    Delay line mode   .  .  .  .  .  .  .  .  .  .  .  .  .  61
             20-2    Table lookup mode .  .  .  .  .  .  .  .  .  .  .  .  .  62
             20-3    Table lookup - rounded  .  .  .  .  .  .  .  .  .  .  .  62

Section 21  Calling the box
             21-1    GET   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  63
             21-2    GIVE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  64
             21-3    BIND  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  64
             21-4    SET_OUTPUT  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  65
             21-5    SET_CHANNEL .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  65
             21-6    SET_PROCEDURE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  65
             21-7    DECODE   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  66
             21-8    RELATIVE .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  66
             21-9    SET_MODE .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  67
             21-10   SET_FIELD   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  67
             21-11   BIND_FIELD  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  68
             21-12   Size of command buffer  .  .  .  .  .  .  .  .  .  .  .  69
             21-13   LOAD_DELAY  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  69
             21-14   INITIALIZE  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  69
             21-15   FLUSH .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  70

                                    Page iii
LRNSAM                         Table of Contents                           DRAFT


             21-16   Processing element arrays  .  .  .  .  .  .  .  .  .  .  70
             21-17   Tick counters  .  .  .  .  .  .  .  .  .  .  .  .  .  .  71
             21-18   Steps in calling the box   .  .  .  .  .  .  .  .  .  .  71

Appendix A  An introduction to pipelining .  .  .  .  .  .  .  .  .  .  .  .  72

Appendix B  Time division multiplexing .  .  .  .  .  .  .  .  .  .  .  .  .  74

Appendix C  Basic number theory  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  76

Appendix D  SAIL examples  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  80
             D-1     Generator example .  .  .  .  .  .  .  .  .  .  .  .  .  80
             D-2     Modifier example  .  .  .  .  .  .  .  .  .  .  .  .  .  82

Appendix E  Delay line example   .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  91
             E-1     Writing your own procedures   .  .  .  .  .  .  .  .  .  92

Appendix F  Generator field and mode tables  .  .  .  .  .  .  .  .  .  .  .  94
             F-1     Generator parameters .  .  .  .  .  .  .  .  .  .  .  .  94
             F-2     Generator run modes  .  .  .  .  .  .  .  .  .  .  .  .  94
             F-3     Oscillator modes  .  .  .  .  .  .  .  .  .  .  .  .  .  95
             F-4     Envelope modes .  .  .  .  .  .  .  .  .  .  .  .  .  .  95

Appendix G  Modifier field and mode tables   .  .  .  .  .  .  .  .  .  .  .  96
             G-1     Modifier parameters  .  .  .  .  .  .  .  .  .  .  .  .  96
             G-2     Modifier procedures  .  .  .  .  .  .  .  .  .  .  .  .  97

Appendix H  Delay unit field and mode tables .  .  .  .  .  .  .  .  .  .  .  98
             H-1     Delay units: timing  .  .  .  .  .  .  .  .  .  .  .  .  98

Appendix I  Reserved Words .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  99

Appendix J  Error messages .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 103
















                                    Page iv
LRNSAM                                                                     DRAFT


                                   Section 1
                                   _______ _

                                  Introduction
                                  ____________





1-1. The nature of the problem

The Samson  box creates sounds  by producing streams  of binary  numbers, called
samples, at a  blinding rate of speed.   The process includes digital  to analog
conversion which changes these samples into analog voltages that  are eventually
fed to a  sound system.  There  is a need for  extreme speed in  calculating the
samples which can  be appreciated if you  realize that according to  the Nyquist
Sampling Theorem, one cannot represent  a frequency higher than 1/2 of  the rate
at which the samples are  produced (called the sampling rate).  This  means that
for reasonably good hi-fi sound, where the frequency response might go up  to 15
kHz or beyond, the  machine must be capable of  at least a 30 kHz  sampling rate
per channel of sound.  This can be easy or difficult to do depending on how much
computation goes  into each sample.   But typically, each  sample is made  up of
great amounts of computation such that the speed limits of most modern computers
can  be  quickly  surpassed, especially  considering  that  music  production by
computers typically requires a great  deal of sophistication in the  models used
to generate interesting sounds.  So you can see that to get a good fast sampling
rate and still have lots of time  left over to compute each sample we've  got to
be clever in how we do things.  The Samson box is a study in cleverness.



1-2. Reading this manual

This manual  starts out assuming  an elementary knowlege  of the  basic computer
science concepts of pipelining  and time division multiplexing.  If  these terms
are unfamiliar to you you may want  to first read appendicies 1 and 2  which are
designed to  impart an understanding  of these things  to the (not  quite total)
novice user.   If you  would like to  review basic  digital number  theory, read
appendix 3.

The later sections  on calling the  Samson box from  a SAIL environment  and the
subsequent program examples  are designed to be  read by those  already familiar
with the  SAIL language.   Generally, if you  are only  interested in  the basic
processing of the box read until section 21.






                                     Page 1
LRNSAM                                                                     DRAFT


                                   Section 2
                                   _______ _

                                  Definitions
                                  ___________



Before proceeding let's define  some terms.  These definitions are  elaborted in
greater detail in later sections of this manual.

The Samson box has two basic active parts called processing elements.  The first
element is called  a generator, (see  section 9) the  second is a  modifier (see
section 17).  There are 256 generators and 128 modifiers.

The processing  elements communicate with  eachother through a  scratchpad read-
alter memory called sum memory which is used to store the output from  and input
to the processing elements.  There are 256 locations in sum memory.  See section
8.

Another processing  element, called  delay units, can  only work  in conjunction
with modifiers.  It can reference  another 48k word memory called  delay memory.
This is for reverberation and general table lookups. See section 20.

The amount of time that it takes for the box to produce one cycle of output from
all the running modifiers and generators is called a pass.  A pass is made up of
a series of ticks.  A tick is the smallest counting unit in the box.  It  is the
amount  of  time  necessary  for  the  slowest  basic  process  to  complete its
operation.  There  are two  basic types  of ticks.   Processing ticks  count the
computations in time  per pass of the  processing elements.  Update  ticks count
the number  of new values  passed to the  box in time  per pass.  Both  kinds of
ticks take  the same amount  of time.   In other words,  when sound  samples are
being computed we are counting in processing ticks, when we are updating the box
with new parameters we are using update ticks.  The sum of all  processing ticks
and all update ticks equals a pass.  See section 3.

A pass  is not  to be  confused with a  sample.  A  sample is  the result  of an
individual sound pressure wave calculation.  If the box is producing one channel
of  music,  then there  will  be one  sample  of sound  produced  per  pass.  So
therefore,  the  number of  samples  produced in  a  pass equals  the  number of
channels outputing sounds.










                                     Page 2
LRNSAM                                                                     DRAFT


                                   Section 3
                                   _______ _

                       Processing ticks and update ticks
                       __________ _____ ___ ______ _____



Now let's take an  example that will show how  the two kinds of  ticks interact.
Since a pass is the sum of processing and update ticks, we must first anticipate
the maximum number of processing  element parameters we will want to  change per
pass.  This is the  number of update ticks  required. It takes one  tick-time to
update  one parameter  in a  processing element.   Then we  figure out  how many
processing ticks it  will take to  allow all the  procesing elements we  want to
run.  Each  running processing  element takes  9 tick-times.   The sum  of these
numbers is the total time in ticks  the box will take to complete one  pass.  We
foreward the total number of ticks  thus calculated to the box on a  command and
the length of a pass is  set accordingly.  When the box runs, it  first executes
all the processing  ticks, then the  update ticks are run.   So when the  box is
cranked up, it first enters the processing mode and it will act on  the existing
numbers in the  processing elements, then it  enters update mode and  will allow
one variable inside one processing element to be changed on each succeeding tick
until the end of the pass.

Please  note the  that update  ticks have  the lowest  priority.  If  we  end up
needing to  update more  parameters on one  pass than  we have  update tick-time
available then those updates will not  happen on this pass, but will  be snarfed
according to how many update ticks there are.  That may or may not be reasonable
depending on what you're doing.  The obvious thing to do is to make a compromise
between  the minimum  necessary  update ticks  and the  longest  acceptable pass
period to produce an acceptable sampling rate.

Lets take as an example  a generator that has two user-settable  locations.  One
location contains the number that represents the frequency.  The  other location
contains a number which changes  the number in the frequency location  by adding
its value to the number in the frequency location on every pass, thereby raising
or lowering the frequency uniformly by some amount or "slewing" it,  producing a
glissando.  We will diagram it this way:
                                SLEW
                                 ↓
                                 + ←←←←←←
                                 ↓       ↑
                              FREQUENCY  ↑
                                 ↓       ↑
                                 ↓ →→→→→→
                                 ↓
                              GENERATOR
                                 ↓
                               samples


                                     Page 3
LRNSAM                 Processing ticks and update ticks                   DRAFT


                                     Fig. 1
                                     ____ _


On every pass the  number in FREQUENCY is  sent to the GENERATOR  which produces
the sample passed as the output.  But the FREQUENCY number is also  piped around
and added into the number coming down from SLEW.  The sum of FREQUENCY  and SLEW
replaces the old value in FREQUENCY.  But SLEW doesn't change, it's  a constant.
You can  see that  this thing can  run forever  by itself.  On  each pass  a new
frequency is formed and a new sample is shipped out.

Since we'd like to be able to change its values from time to time,  lets declare
that there will be two ticks per pass, one processing tick, and one update tick.
As  we begin  looking at  the  processing stream,  we notice  it's  already been
running and that the last output was 1000 and SLEW had 1 in it.  If this went on
as  is,  we  would  send succeeding  values  of  1001,1002,1003...  etc.  to the
GENERATOR.  But let's  say that we're  tired of this and  want to change  it, so
we'll put 3 in SLEW at our first chance (where the first vertical arrow is), and
we'll change FREQUENCY to 500 at our next chance (at the second arrow).  The two
rows SLEW and FREQ. in the next figure show the progression of values of the two
settable locations in our generator.   The bottom line shows what  frequency the
generator is actually producing on that pass.

        |<--- pass -->|<-- pass --->|<--- pass -->|<--- pass -->|
        |             |         ↓   |        ↓    |             |
        |proc| |upd.| |proc| |UPD.| |proc| |UPD.| |proc| |upd.| |
        |____|_|____|_|____|_|____|_|____|_|____|_|____|_|____| |
Slew    |   1| |   1| |   1| |   3| |   3| |   3| |   3| |   3| |
        |____|_|____|_|____|_|____|_|____|_|____|_|____|_|____| |
Freq.   |1001| |1001| |1002| |1002| |1005| | 500| | 503| | 503| |
        |____|_|____|_|____|_|____|_|____|_|____|_|____|_|____| |
Out     |1000|        |1001|        |1002|        |1005|        |503|...

                                     Fig. 2
                                     ____ _
















                                     Page 4
LRNSAM                                                                     DRAFT


                                   Section 4
                                   _______ _

                             Tick time requirements
                             ____ ____ ____________





4-1. Generator tick time

Each running generator requires one processing tick; the length of the generator
pipeline is 9 ticks.

So if we had, say, 11 generators running, and assumed for the moment  that there
were no update ticks, how many ticks would it take before we got a  complete set
of valid results for all the  generators?  That is to say, when would  the first
pass end?  It would end after 20 ticks, right?  If not right, go back and reread
the part about pipelining again in appendix 1.  When would the next pass and all
subsequent passes end?  Right,...  after 11 more ticks.



4-2. Modifier tick time

As I  mentioned, modifiers do  general numerical and  logic processing.   As the
name implies, they modify existing numbers.  In fact, they are supplied with two
number suckers and one number spitter.  Having to do this many memory references
(three  per  cycle, two  reads  and one  write)  and  additional multiplications
require twice as much time as generators, so each modifier takes  two processing
ticks, whereas  each generator only  requires one.  However,  the length  of the
modifier pipeline is also nine ticks.  So say we had 7 running modifiers with no
update ticks, how long until the first complete set of valid numbers  comes out?
The answer is 23.  So, the tick requirement for modifiers is:

   2*<# of modifiers> + 9.                                                Equ. 1



4-3. Combined tick times

Now let's take  an example where we  have both modifiers and  generators running
simultaneously (still without update ticks).  How many processing ticks  for the
first  pass would  be required  if we  have, say,  11 running  generators  and 5
running modifiers?  Before answering this, remember that there are  actually two
processors which represent modifiers and generators.  These processors  are, for
all  intents and  purposes,  seperate nests  of electronics  capable  of running
simultaneously.  The correct answer is therefore 20 ticks.  The tick requirement


                                     Page 5
LRNSAM                       Tick time requirements                        DRAFT


for the first complete set of modifier output is 19, but that for  generators is
20.  Since they are parallel  processes, the modifiers can do their  whole thing
in 19 ticks while the generators are also running.



4-4. General formula

So the official formula reads as follows, and I quote: "The number of processing
ticks is nine more than the maximum of: the number of generators used; twice the
number of modifiers used."(1) Or,

total proceessing ticks =
        2*<# modifiers used> MAX <# generators used> + 9.                 Equ. 2

The Delay units have their own formula for tick requirements which is  not shown
here.  See Appendix 8.





























_______________________
(1) Systems Concepts Digital Synthesizer Programming Specification .
    _______ ________ _______ ___________ ___________ _____________ _

                                     Page 6
LRNSAM                                                                     DRAFT


                                   Section 5
                                   _______ _

                         Processing speed vs. capacity
                         __________ _____ ___ ________





5-1. Speed of processing

The number of ticks per pass is set by the programmer.  This is so because there
is a playoff between passes and the two kinds of ticks.  The nut of  the problem
is that, when all  is said and done, the  basic speed of processing one  tick is
                                                    -7
195 nsec.   (That's .0000000195 sec.,  or 1.95  x 10  , or  195 billionths  of a
second.) That's the time  that it takes a  processing element to do  the slowest
thing it  has to  do.  So  the whole  box is  geared to  go at  this rate  by an
internal clock.  (This means that the maximum number of ticks we can have in one
second is 5,128,205.128.)

Now,  as you  recall, the  whole purpose  of the  box is  to produce  bunches of
numbers which are passed at blinding speeds to the DAC.  Each number sent to the
DAC represents a complex calculation  of the value of whatever waveform  the box
is  producing.  Taken  through  time, the  individual  samples sent  to  the DAC
produce  the desired  waveform.  The  samples must  be passed  to the  DAC  at a
uniform  rate, and  at  a sufficiently  high  speed so  as  to get  a  good high
frequency response.  For instance, a typical sampling rate of 12800  samples per
second produces  a usable frequency  response from 0  to 6400 Hz.   Faster rates
produce higher frequency  responses.  So naturally we  want a good  fat sampling
rate.  But we've got to watch out for a catch, namely, that the box can  only do
so much so fast.  The barrier is the 195 nsec. tick turn around time.



5-2. Ticks per pass

The problem should become clear when we  try to feed samples to the DAC  in real
time.  Real time, in this example  means that we will attempt to  guarantee that
Sam will send 12800  samples to the DAC per  second.  That means one  pass takes
           -5
7.8125 x 10   seconds.   Furthermore, Sam must send  them at an even  rate, such
that each  pass gets computed  and each  sample shipped out  in 1/12800'th  of a
second.  How many  ticks can we  squeeze out of this  much time?  That  comes to
           -5              -7
7.8125 x 10    / 1.95 x  10   = 400  ticks per pass.   This then is  the maximum


                                     Page 7
LRNSAM                   Processing speed vs. capacity                     DRAFT


number of  ticks of  either kind  that can be  squeezed into  a pass.   That's a
pretty  good number,  actually.  That  means we  could have  all  the generators
running using up 256 ticks and still  have 144 left as update ticks.  But  if we
were really greedy, and wanted even more ticks than this at this  sampling rate,
we would  no longer  be able to  guarantee a  sample on each  pass and  we would
sooner or later  (probabily sooner) fall behind.   This is called  data underrun
and when this happens, we drop out of real time.  Another way to say it  is: you
drop out  of real time  if the duration  of the pass  period is larger  than the
sample period.

This is called processing degradation.  It's not altogether catastrophic in that
there are several ways to  suffer this gracefully if it becomes  necessary, such
as to  1) lower  the sampling rate  thereby increasing  the sampling  period and
hence the pass period, 2)  lower the complexity of the process  thereby reducing
the number of ticks required and shrinking the pass period, or 3) to accept less
than real time processing and pass the samples to some form of memory (probabily
the disk) from whence they can be played as a whole when the processing is done.
(Even here there are limitations in  that the disk bandwidth, i.e.  the  rate of
data transfer, becomes  your limitation.  Disk I/O  is currently the  number one
bottleneck in the A.I. system.)



5-3. Processing vs. update ticks

Not only is there a playoff between the desire to have the sampling rate as fast
as possible and the desire to have moby amounts of ticks per pass, but  there is
a playoff between the  two kinds of ticks,  to wit: Processing ticks  and update
ticks occupy mutually  conflicting time domains.   All the processing  ticks are
performed first,  then what's left  is given  to the update  ticks.  So  if your
piece requires lots  of real time user  interaction with keyboards or  what have
you,  or if  you are  repatching  the connections  and modes  of  the processing
elements all the time, then you will be forced to allocate a larger part of each
pass to sending  update information to  the box, and  that means lots  of update
ticks per pass.   And since no  processing can be  done in a  processing element
while it is having its parameters updated, this reduces the number of processing
ticks  available on  any given  sample, and  means that  the instrument  must be
simpler than one which had fewer  update ticks.  The obverse works as  well: too
complex an instrument means you have fewer update ticks per sample.  So,  as old
Richard Almaniac used to say, "budget your ticks wisely!"



5-4. Budgeting ticks

Now when I said above that the  user sets the number of ticks, what  that really
comes down to is that the user  sets two numbers, the total number of  ticks per


                                     Page 8
LRNSAM                   Processing speed vs. capacity                     DRAFT


pass,  and the  total number  of those  ticks which  are processing  ticks.  The
remainder are, of  course, update ticks.  Now  in figuring this number,  we will
ignore  the nine  ticks tacked  on  to represent  the length  of  the processing
element pipeline.  Sam takes care  of that for you automatically.  So  we really
only need  to add up  the number of  generators used, take  the maximum  of this
number or  twice the number  of modifiers,  plus the number  of update  ticks we
expect to need.  This will get the number that we will actually send off  to the
box to be  the total number of  ticks per pass.  Sam  will take this  number and
compute the  sampling rate that  it equals, and  set up  everything accordingly.
(Actually, the number  arrived at in  this way may or  may not correspond  to an
existing low  pass filter implemented  in the  box.  In that  case, you  get the
filter whose cutoff  frequency is less  than or equal  to 1/2 the  sampling rate
calculated as above).



5-5. Numbering Processing elements

One final  word of  note: the  processing elements  are all  numbered ordinally.
Generators go from 0 to 255 modifiers go from 0 to 127. Processing always begins
with the lowest numbered running processing element and goes  sequentially until
the end of the pass.  Since speed is typically such an important commodity here,
it  is  important  to  always  use  the  lowest  numbered  consecutive  block of
processing elements as possible.  If you have a processing element in there that
is really not being used but its number is below the highest one in use, you are
wasting compute time (sinful!).























                                     Page 9
LRNSAM                                                                     DRAFT


                                   Section 6
                                   _______ _

                            The processing elements
                            ___ __________ ________



As you have seen, Sam has two basic types of processing elements: generators and
modifiers.  There is an array of  memory locations called sum memory in  the box
which  these processing  elements use  to pass  numbers among  themselves.  Also
contained within  Sam is  a large memory  called delay  memory.  This  memory is
accessed by the modifiers  and can be used as  delay lines (hence the  name) for
realizing  filters and  reverberators.  The  delay memory  can also  function as
table lookup memory allowing various computer-loaded functions and tables  to be
easily accommodated.



6-1. Generators

The generators peform two  functions: waveform generation and  dynamic amplitude
control  of the  generated  waveform.  The  portion that  performs  the waveform
generation is called the oscillator, and the portion that handles  the amplitude
control  we'll  call  the  envelope  controller.   Generators  can  produce sine
(cosine), square and saw-tooth waveforms, pulse train, and  equal-amplitude sum-
of-cosines (band limited pulse trains).  They can be frequency modulated  by the
output of  any other  processing element, and  the frequency  can also  be swept
linearly.  The envelope side has facility to shape amplitude using either linear
or exponential  curves.  Sam  has 256 generators,  and they  can all  be running
simultaneously.   Which  of  these  various  operations  the  generator  does is
dependent on the generator's mode, which is set by the number of update ticks.



6-2. Modifiers

Modifiers can  act as filters,  amplitude modulators,  four-quadrant multipliers
(signed multiply), sample mixers  and uniform noise generators (white  noise and
random number generation). They can  also do signal latching (sample  and hold),
sign testing, signal zero-crossing detection, and minimum or maximum testing.

Modifiers  are  also  used  to  pass data  to  and  from  the  delay  memory for
reverberation or table lookup.  Sam  contains 128 modifiers all of which  can be
running at once.






                                    Page 10
LRNSAM                      The processing elements                        DRAFT


6-3. Sum memory

Information is passed among generators and modifiers through what is  called sum
memory.  This can be thought of  as a kind of telephone switch-board  because it
is through the  sum memory that  the data put out  by one processing  element is
accessed by other processing elements.



6-4. Delay memory

Delay memory  has two uses:  It can simulate  delay lines for  reverberation and
filtering, and it can store precomputed tables, such as time-domain waveforms or
mathematical functions that would not be possible (on our budget) to  have built
in.  There are 32  delay lines and they can  all be running at once.   The delay
line is actually only a conceptual  component of a delay unit.  A delay  unit is
formed from a modifier  which has been put into  the proper mode and  told which
area of delay memory to use as  its delay line.  So every delay unit  also costs
one modifier.






























                                    Page 11
LRNSAM                                                                     DRAFT


BLKDIA.XIP[DOC,MUS] goes here.
















































                                    Page 12
LRNSAM                                                                     DRAFT


                                   Section 7
                                   _______ _

                                The flow of data
                                ___ ____ __ ____





7-1. The Connection...

The interface of the Samson box to  the world at large is roughly this:  we, the
users, speak to the PDP-10 which  runs the AI timesharing system and  the PDP-10
speaks  to the  Samson  box.  It's  not  that simple,  there's  another computer
between the 10 and the box, but since it is transparent to the  overall process,
we'll leave it out here. Also connected to the Samson box are the DACs  that get
the samples the box produces.

The following is  a list of the  different kinds of things  we would like  to be
able to pass between these devices: we will need to be able to 1)  send commands
from the  general timesharing  system, 2) read  back diagnostic  information, 3)
read and write sound  samples from and to the  system, and finally, 4)  to write
samples to DACs.  But  before we go into greater  detail, a caveat must  be made
about  the  discussion  of  the  first three  items.   What  is  presented  is a
description of a "typical" usage of the Samson box rather than a  description of
what actually is being done at SAIL because the actual hardware  connection here
is likely  to go  through many transformations  in the  course of  time, besides
which a detailed review of the subject is beyond the scope of this manual.  (For
those interested, or if your work requires it, see a wizard.)



7-2. Command path

We  will need  a command  path from  the PDP-10  to the  box to  pass processing
element instructions, like start  and stop instructions, connection  plans, mode
changes, and the  actual data to  be put in the  processing elements to  set the
frequency of generators, etc.  All  of this information is actually  absorbed by
Sam during update  ticks.  To do  this there is  a direct memory  access channel
(DMA)  set up  between the  box and  the main  core memory  in the  PDP-10.  The
central processing unit  (CPU) in the  PDP-10 first finds  a free block  of core
memory, then fills it with instructions and update data for Sam, then passes the
address to Sam.  Then  Sam, during the processing  of update ticks, reads  it in
through the DMA channel.






                                    Page 13
LRNSAM                          The flow of data                           DRAFT


7-3. I/O path

Next, we will need a way to set the general status of the box, to turn it on and
off, and to  pass it the  address of the core  memory discussed above.   This is
called the I/O (for in/out) path.  It is a relatively slower path than  the DMA.
It is also used for diagnostic readback.



7-4. Read path and Write path

It would be nice to be able to  pass sound samples to and from the box  from the
PDP-10.  For this, we have the "read" path and the "write" path.  Each  of these
has its own main core memory  address like the command path above, and  they use
the DMA channel.



7-5. DAC path

Lastly, how do we get samples from Sam to the DACs?  Before I can answer that, I
must elaborate a bit on how samples  are handled and how they are stored  in the
box.  Remember that the output of the processing elements ends up in sum memory.
Well, the only things that can  talk to sum memory are the  processing elements.
The PDP-10 has no way of directly sticking a number in sum memory or  taking one
out.  So how does  it work?  Well, it turns  out that the generators can  be put
into a special  mode such that  they act as data  channels.  (See page  24).  In
this mode they do not oscillate at all.  They simply stand at the Samson box end
of the read and  write path from the PDP-10.   In this special mode  they either
receive data from the read path and put it down somewhere in sum memory, or they
take data from somewhere in sum memory and put it in the write path.  But that's
not all.   They have  yet another  funny mode  (again, one  in which  they don't
oscillate) in which they act as data  paths to the DACs.  (See page 24).   So it
falls to the  generators to do  all the passing of  data between sum  memory and
both the 10 and the DACs.



7-6. It's a Stream machine...

This is an appropriate time to  mention that the box is a "stream"  type device,
meaning that it  can be running whether  you are telling it  what to do  or not.
You don't have to fill up all  the update ticks that you've asked for.   You can
send less PROVIDED that you tell  Sam not to expect any more  update information
until some time which  you specify as being so  many ticks in the  future.  This
number can  either be relative  from the  first tick or  from the  current tick.
Thus you can set up some state in the box and tell it to continue doing  what it


                                    Page 14
LRNSAM                          The flow of data                           DRAFT


is set up to  do.  The way to  do this is to give  it a DWELL command  (See page
68).  This command is passed to the  box on an update tick and it  instructs Sam
not to process any more update commands until it has done so many ticks.  If you
don't  give  Sam  an  instruction when  it  expects  one,  it's  called "command
underrun", and it can stop the  box.  Sam then sends an interrupt to  the system
which halts your program.  Actually, any process that gets out of sync  with its
environment will cause or  suffer underrun or overrun.  There's  less likelihood
that anything  inside the box  would get  out of order  (heaven help  us!), than
between the  system and the  Samson box where  things can get  out of  hand very
easily.  If Sam is trying to get commands while the system is looking elsewhere,
or if there is a disk  bottleneck (not unlikely) then you get  command underrun.
There are  other data errors  that can also  send interrupts such  as arithmetic
overflow  and problems  relating to  direct memory  access.  All  interrupts are
handled through the I/O path.



































                                    Page 15
LRNSAM                                                                     DRAFT


                                   Section 8
                                   _______ _

                                   Sum memory
                                   ___ ______



Sum  memory  is  the place  that  numbers  are stored  that  are  the  output of
processing elements and are to be the input to other processing elements.  It is
necessary that a value  be read into a location  in sum memory before it  can be
read by  any other  processing element.   Sum memory is  made up  of a  bunch of
locations, each of which has its own address.  There are 256  locations numbered
from 0 to 255.  These addresses are passed to those processing elements that are
to read or write a number into that sum memory location.

It's called sum memory because any value added in during a pass is added  to the
contents already there from the beginning of that pass.



8-1. This pass - last pass division

Sum memory is divided in two  basic ways, producing four quadrants.  One  of the
divisions is  between "this pass"  sum memory and  "last pass" sum  memory.  The
active processing  elements on  the current pass  all write  into this  pass sum
memory and read  from last pass.   The reason it is  divided into this  pass and
last pass is because  memories (that we can  afford) aren't fast enough  to read
and write into the same place all in 195 nsec.  This means that all  the outputs
of any given pass are in place before any reading of sum memory is  done.  After
the processing of all the  outputs has taken place (which, you  should remember,
is the definition of  a pass), this pass sum  memory is frozen and  becomes last
pass sum memory.   This is nice in  that it means that  you don't have  to worry
about the order of computation of the processing elements inside of a  pass.  By
the time you get around to reading the output of any processing element  all the
outputs of all the processing elements are available, ensconced in last pass sum
memory.

However, there is a special feature that modifiers can also read from  this pass
sum memory (not  to make things  too confusing or  anything), in which  case the
ordering of the modifiers inside a pass does become critical.  If, for instance,
                                        ____
the modifier that is trying to read a this pass sum memory location  is executed
before the one that is supposed to be supplying the number, you get zeros.

Please notice an implication  of the separation in  time by one pass  of reading
and writing into sum memory.  Whereas the processing order within a pass  is not
critical  (with the  exception above  noted), the  processing between  passes is
                                                                              __
critical if you are chaining a bunch of processing elements together.
Say maybe you want  to FM modulate an  FM modulated generator, meaning  that you


                                    Page 16
LRNSAM                             Sum memory                              DRAFT


have the output  of one generator affecting  the frequency of  another generator
affecting the  frequency of yet  another generator.  This  setup is  yet another
pipeline!  So you must be sure to start the processing elements in  order.  Pass
1: the three generators crank and  from generator 1 comes a valid number,  but 2
and 3 produce nonsense; pass 2: generator 2 reads generator 1's output and gives
a valid  result; pass 3:  finally the valid  results have trickeled  through the
system.  Had we  not started up  the generators in order,  we would have  lost a
sample and  promulgated the  delay down  through the  line.  It  may or  may not
produce a fatal error, but it is surely a potential bug.

The last pass and  this pass halves of sum  memory alternate, one set  being the
current this pass, then becoming  last pass.  Before it again becomes  this pass
it is  erased so  that previous values  don't linger.   Therefore reading  a sum
memory location without writing in it first will always return zero.



8-2. Generator - modifier division

The other basic division of sum  memory has to do with which kind  of processing
element writes where.

One half of "this pass" sum memory is dedicated to output from generators.  That
means the generators  have 64 locations  in this pass  sum memory that  they can
write into.  The other  half of  this pass  sum memory  (64 locations  again) is
dedicated to the output of modifiers.  For this pass sum memory this distinction
remains firm.  However, either type of processing element can read  any location
                                                              ____
(128 of them) in last pass sum memory.

The modifiers have the ability, you  recall, to read from this pass  sum memory,
however, they are restricted to reading from the modifier quadrant.   That makes
a total of  184 locations that are  available for them to  read from on  a pass.
This can be so  since a modifier only does  one write into sum memory  every two
ticks. So it has some extra time  to do sum memory reads.  Again, if  a modifier
is  reading from  this  pass memory,  the ordering  of  the units  must  be done
carefully so that  one is assured  that the data is  present in memory  when you
want to read it. This requires keeping track of the extensive pipelining  in the
machine, which we will not go into here.



8-3. Addressing sum memory

Here's what the  processing elements have to  communicate with sum  memory: Each
generator  that is  running is  given a  location in  sum memory  into  which it
deposits its  generated sample.  More  precisely, there is  a location  in every
running generator which contains a word that is the address of a location in sum


                                    Page 17
LRNSAM                             Sum memory                              DRAFT


memory into  which that generator  is to  put its output.   The location  in the
generator that stores  the sum memory  address is called  (appropriately enough)
SUM_MEMORY.  Generator location SUM_MEMORY  is therefore called a "port"  to sum
memory.  Generators likewise  have one port to  read from sum memory  called FM.
This means that FM is a location in the generator which contains a word which is
an address of a location in sum memory from which the generator is to read data.

The modifiers have one port to write, also also called SUM_MEMORY (the assembler
can make the distinction of whether we are referring to the  SUM_MEMORY location
in a modifier or generator as  will be explained later), and two ports  to read,
A_IN and B_IN.

The delay units have no direct port  to the sum memory, but must be  accessed by
the modifiers, which pass data to  the delay units and read data from  them. The
modifiers then transfer data to sum memory.


































                                    Page 18
LRNSAM                                                                     DRAFT


                                   Section 9
                                   _______ _

                                   Generators
                                   __________



Generators are one of two main processing elements in the box.  They  do various
things, most important  of which, they  produce oscillations of  various flavors
and frequencies and they control the amplitude of the oscillations through time.
The generators are divided into two parts: the oscillator is the side that makes
the waveform, and the side that controls the amplitude of the waveform is called
the envelope controller, or just envelope.

The generators generate waveforms and envelopes by two basic methods  1) logical
testing of a changing function (as  in the case of pulse train and  square wave)
or 2) table  lookup (as in  the case of  sine waves and  exponential envelopes).
The oscillator  looks up  values from a  sine table  to produce  sinusoidal (and
cosinusoidal) waveforms.  There are  several things it must determine  before it
knows where  to look in  the table,  most obviously what  the frequency  is, the
phase, etc., etc.

The information the generator needs to  produce its waveform is passed to  it on
update ticks.  The values sent  are stored in locations in the  generators, from
whence  they  are  combined  by  processing  ticks  to  produce   the  waveform.
Generators can  receive new parameters  for all of  the locations listed  in the
table below on the  processing of one update  tick.  Depending on what  mode the
generator is  in, not all  of this information  may be needed  or useful  at any
given time.  In  addition, some locations have  multiple functions and  are used
for different things  in different modes.  (For  instance, if you just  want the
generator to produce sine (cosine) waves, you need not specify NCOSINES.  If you
don't care about the initial phase of the wave, you need not specify ANGLE, etc.
More on this later.)

Although the oscillator part of the generator will be covered more  fully later,
a little of its operating  theory is necessary before discussing  its variables.
The heart of the  oscillator side of the generator  is the ANGLE term.   This is
the index which keeps track of where we are in the cycle of whatever waveform we
are producing.  Angle is what is tested or used for table lookups that determine
the value of the  sample.  ANGLE should be  thought of as an  unsigned binary(1)
counting register that, as it is incremented from its lowest number  (all zeros)
to  its highest  (all ones),  represents the  phase angle  of one  cycle  of the
waveform being generated.  The phase  angle of zero is represented by  all zeros
in the register, pi/2 is represented by 010000... (decimal.25, i.e.  one quarter
of a cycle), pi by 10000...(decimal .5, the half-wave point), and when  ANGLE is

_______________________
(1) for a discussion of number representations used in this manual  see appendix
3.

                                    Page 19
LRNSAM                             Generators                              DRAFT


all ones,  the phase  angle is  about ready  to start  another cycle.   ANGLE is
mostly formed of the  number in FREQUENCY plus  whatever was in ANGLE  from last
pass.  As  FREQUENCY increments ANGLE,  ANGLE rises until  it reaches  the point
where the  next value from  FREQUENCY would overflow  the register,  then modulo
arithmetic sets in as follows:  the ANGLE register is cleared and  the remainder
magicly appears as its new value, and  we have started a new cycle of  our wave.
Thus the size  of the number in  FREQUENCY determines the  frequency (oftenness)
that ANGLE turns over.  The EXPONENT  term in the oscillator side does  much the
same thing as ANGLE.  All this is explained in greater detail later.



9-1. Generator parameters

In the tables below, there are  generally two names for each location.  There is
the internal name (denoted  by the column marked Id)  which is the name  used in
the  hardware description  of the  device and  on the  circuit diagrams  for the
device, then there  is the SAIL name  (denoted by the column  marked Definition)
which is how we refer to these things in SAIL.  I will refer alternately  to the
Id name  and the  Definition in the  discussion of  these things  later, usually
puting the Id  in parenthesis after  the SAIL name,  to acquaint you  with both.
But  you should  understand that  the Definition  is what  you will  use  in any
                                      __________
program to refer to the particular  location in the box, and the  assembler that
actually runs the  box will convert  it into the  equivalent Id code.   The Size
column tells  how many binary  bits the location  consists of.  The  Type column
says whether the  number is: Signed, Unsigned,  Different in different  modes or
                             _       _          _
Inconsequential  (can  be considered  either).   The notations  in  the Function
_______________
column are elaborated more thoroughly later.





















                                    Page 20
LRNSAM                             Generators                              DRAFT


Id    Size Type Definition Function
--------------------------------------------------------------------------------
GO    20   S    SWEEP      oscillator frequency sweep rate
GJ    28   I    FREQUENCY  oscillator frequency
GK    20   D    ANGLE      oscillator angle
GN    11   U    NCOSINES   number of cosines to be summed
GM    4    U    SCALE      binary scale of cosine or sum of cosines
GP    20   S    RATE       envelope rate of change
GQ    24   U    EXPONENT   envelope current value
GL    12   U    ASYMPTOTE  asymptote (DC offset of the envelope)
GSUM  6    U    SUM_MEMORY sum memory location into which output is added
GFM   7    U    FM         sum memory address from which frequency
                           modulation data is taken
GMODE 10        MODE       generator mode, including run mode and
                           type of oscillator and envelope processing
                           (see list of modes on page 22).
                OSC_MODE   sets oscillator field of SUM_MEMORY without
                           altering run mode or envelope mode.
                ENVELOPE   sets envelope field of SUM_MEMORY without
                           altering run mode or oscillator mode.

                                     Fig. 3
                                     ____ _



























                                    Page 21
LRNSAM                                                                     DRAFT


                                   Section 10
                                   _______ __

                              Generator run modes
                              _________ ___ _____



This is  a list  of general modes,  affecting both  the envelope  and oscillator
halves of the generator.  There  are some additional modes that are  specific to
the two halves which we'll get to  in good time.  The run modes are set  up with
update ticks.  Since the hardware names for the run modes are binary words, only
the SAIL definitions are given.

Definition         Osc. run? Envelope run? Add to sum?
--------------------------------------------------------------------------------
G_INACTIVE         no        no            no
G_PAUSE            no        no            no
G_WAIT             yes       no            no
x_RUNNING:
   A_RUNNING       yes       yes           yes
   B_RUNNING       yes       yes           yes
   C_RUNNING       yes       yes           yes
DATA_READ          no        yes           yes
DATA_WRITE         no        no            no
DAC_WRITE          no        no            no

                                     Fig. 4
                                     ____ _




10-1. INACTIVE

freezes  both  the oscillator  part  of  the generator  and  the  envelope side.
Actually,  the  generator  continues  to  be  processed,  but  the  changing  of
parameters is inhibited and it produces no output.  Please note that  this means
making  a generator  INACTIVE does  not clear  its memory.  It retains  the last
frequency and envelope it had  before going inactive. In fact,  generator memory
is  never cleared  except by  explicit  command to  load new  values.   For this
reason, you have to  set all the relevant  parameters of a generator  before you
turn it on, else you might be subject to a certain form of astonishment.



10-2. G_PAUSE

freezes both  the oscillator  and the envelope  as does  INACTIVE, but  makes it
easier to restart it.  There is a special command called "clear all  pause bits"


                                    Page 22
LRNSAM                        Generator run modes                          DRAFT


(see page  67) that  will reinstate  all PAUSing  generators, whereas  you can't
reinstate an INACTIVE generator except by an explicit change-mode command.  This
is useful if, for instance, a bunch of generators were running, you want them to
stop,  but you  don't  want to  have to  send  them all  individual  mode change
commands to crank them up one  by one which would waste update ticks.   This way
also you are guaranteed that they will all restart later with the same phase and
envelope positions  as where they  left off.  (But you must  set up  the pausing
generators one per update tick.)



10-3. G_WAIT

continues running the oscillator side but turns off the envelope side  and stops
output. This is to  let the oscillator keep  in phase with any  other generators
that may be running concurrently.   It can therefore reenter later and  still be
in sync.  with the rest  of the world.   There is also  a "clear all  wait bits"
command (see page 67) later that encourages the use of G_WAIT mode.



10-4. x_RUNNING
This is  the principal running  mode.  It has  three slightly different  ways of
doing its thing.  Depending on which one you want, you substitute A, B or  C for
the x.  Basically, the  only difference is in how  the envelope side is  set up,
the oscillator in all three modes just runs as usual.
But first  a word  about stickiness.   Imagine the  following horror  story: the
envelope part of the generator is happily running along cranking out ever louder
samples.  Comes a  time when this  is supposed to  stop, but where's  the update
command?  Stuck somewhere in  the operating system which  is too hung up  to get
the command  out.  Oh no!   The amplitude gets  louder and louder  and louder...
Finally, we hit the largest amplitude the generator can produce  and overflow...
What a bummer...   But look, here comes  stickiness to the  rescue!  (arithmetic
overflow occurs  when the  maximum positive  number is  exceeded or  the maximum
negative number  is exceeded. For  instance, if you  add anything to  the binary
number 11111111111 . . . you get an overflow. Likewise if you  subtract anything
from the binary number 000000000 . . . you get an overflow.)

The envelope side of the generator can be made "sticky," which means that rather
than overflow for lack of a command to tell it to stop, the envelope side of the
generator  will "stick"  at the  last  value it  attained before  it  would have
overflowed. From that point on it will just continue to return that number until
the update arrives  to tell it to  do something else.  Alternately  the envelope
can be set to "free" mode, in which case the envelope wraps around and starts in
at the beginning again, and all ones becomes all zeros and we start rising again
(or all zeros become ones and we continue falling).  A_RUNNING sets the envelope
in sticky mode.  B_RUNNING sets it in free mode.


                                    Page 23
LRNSAM                        Generator run modes                          DRAFT


There's another clever  hack whereby arithmetic overflow  in an envelope  can be
detected and  used as a  "trigger" to cause  the next numbered  (the subsequent)
generator to go from G_WAIT to running.  Meanwhile the triggering generator goes
to G_WAIT mode.  This is what C_RUNNING does.  The purpose of C_RUNNING  is that
by  dedicating,  for instance,  three  sequentially numbered  generators  to one
"instrument", such that one generator  computes the attack, the next  the steady
state, and the  last the decay,  the "instrument" can,  once it is  started, run
without  any  further  update  commands.  This  ties  up  three  generators, but
lightens  the  command  stream  by  two  update  ticks  per  invocation  of that
instrument.  It may also be useful  in cases like the one described  above where
the general system load is such that  the box couldn't get a command in  time to
save a generator from command  underrun.  The user would still have  a guarentee
that at least  the note once started  would continue along a  prescribed course.
Thus we achieve graceful degradation.



10-5. DAC_WRITE

mode makes this oscillator be  a port between the sum memory  location addressed
by the word in FM  and a DAC addressed by the  word in SWEEP (GO).  This  is how
you get sounds  out of the  box.  In this capacity  the generator is  similar to
OUTn locations in MUSCMP.  (MUSCMP  is the generic name for your  favorite music
compiler.) What happens to SWEEP  (GO)?  This is an instance where  the function
of the  generator modifies what  the variables mean.   When the generator  is in
DAC_WRITE mode, it is no longer  a generator in the sense of  producing samples.
It goes into the special mode of taking information from the sum memory location
whose address  is in FM  and passing it  to the DAC  whose location is  in SWEEP
(GO).  All other variables in this generator have no effect in this mode.



10-6. READ_DATA

mode makes the  generator read data from  main computer core memory  through the
DMA channel, and deposit it in the sum memory addressed by SUM_MEMORY.   This is
how we get  sound samples (for  instance) into the  box from some  other device.
The DMA channel is set up with a SET_OUTPUT command (discussed later).   The CPU
figures out how to get the data to the generator.  It is important to  note that
the write data will be interleaved in generator number order.
                                      _________



10-7. WRITE_DATA

makes the generator take samples from a sum memory location and write  them into
main computer core memory through the DMA channel.  This is how sound  files are


                                    Page 24
LRNSAM                        Generator run modes                          DRAFT


written onto the disk.  You set up one generator in WRITE_DATA mode  per channel
of output.  Again, the write data is interleaved in generator number order.
                                                    _________















































                                    Page 25
LRNSAM                                                                     DRAFT


DSK:GENDIA.XIP[DOC,MUS] GOES HERE
















































                                    Page 26
LRNSAM                                                                     DRAFT


                                   Section 11
                                   _______ __

                             Oscillator processing
                             __________ __________



In the diagram on page  26, the left side represents the  oscillator processing,
the right  half the envelope  processing.  SWEEP (GO),  RATE (GP)  and ASYMPTOTE
(GL) are constants  which remain until altered  by command, all  other locations
are  "refreshing",  meaning  that  their  value  is  recomputed  every  time the
generator goes through a processing tick.  The old value is read by  the process
just below  it, and the  old value is  added to the  new value coming  down from
above.  This is diagrammed as an  arrow returning from the bottom to the  top of
such locations such as ANGLE (GK).

If an  oscillator is in  a wave  producing mode, the  following is  a simplified
description of what happens:(1) On the oscillator side, FREQUENCY (GJ) will have
the current frequency of the oscillator. SWEEP (GO) is a constant that  is added
into FREQUENCY (GJ) every pass  to augment or diminish the frequency  to produce
sweeping or glissando.  (If you do not  want any glissando, you  must explicitly
zero SWEEP.)

FM is the return port into the oscillator from the "last pass" sum  memory. When
the  oscillator is  in any  oscillatory  mode (as  opposed to  the  data channel
modes), FM reads  the location in  sum memory and  the returned value  is summed
with FREQUENCY thereby adding a modulating frequency (assuming that  a frequency
was put, on  the last pass,  into the sum memory  location that FM  reads).  The
value in  FREQUENCY is  not changed  by the  addition with  FM.  Note  that each
generator, when in an oscillatory mode, always takes in a number through FM!  If
                                        ______
you don't want anything here, then you  have to give FM the address of  some sum
memory location that is unused, so it will always return zero.

ANGLE (GK) stores the current angle.  It is updated by adding FREQUENCY  (GJ) as
computed above to the number in ANGLE (GK) left from last pass.  ANGLE (GK) then
contains the current oscillator angle.  It is used, depending on the mode of the
oscillator to determine the value of the output sample.  It can be considered to
be the momentary  phase angle of the  waveform the generator is  producing.  But
the angle is not in terms of radians or degrees. It's simpler, actually:  as the
number in ANGLE goes from all 0000...'s to all 11111...'s we complete  one cycle
of our waveform. That  is we go in radians  from 0 to 2π.  After  11111... ANGLE
wraps around to 00000... again.  Thus  when the number in angle is  01000..., we
are  at  the half-wave  point.   Likewise, RATE  increments  EXPONENT  such that
EXPONENT goes from zero to full amplitude as its bits go from 0's to 1's.

When we finally have the value  in ANGLE, what happens next depends on  the mode

_______________________
(1) For the real version, see the Specification.

                                    Page 27
LRNSAM                       Oscillator processing                         DRAFT


of the oscillator.  If it is  in a sine (cosine) wave producing mode,  the ANGLE
(GK) is used to lookup a value  on a sine (cosine) table.  This is then  the end
result of  the oscillator  side. This result  is not  complete, however,  as the
output  of the  oscillator must  still be  scaled by  the envelope  side  of the
generator. The envelope side is discussed  in the next section, and the  rest of
the modes are covered in more detail on page 36.











































                                    Page 28
LRNSAM                                                                     DRAFT


                                   Section 12
                                   _______ __

                                Oscillator modes
                                __________ _____



These modes set the type of  waveform the oscillator side of the  generator will
produce.

   Definition      Function
--------------------------------------------------------------------------------
   SAWTOOTH        sawtooth wave
   SQUARE          square wave
   SUM_OF_COSINES  sum of cosines
   PULSE_TRAIN     pulse train
   COSINE          sine(ANGLE),normal sine wave
                   mode (with fm).
   COS_FM          cosine(FREQUENCY + FM)
                   special sine table lookup mode.

                                     Fig. 6
                                     ____ _




12-1. COSINE

This mode is the standard sine wave oscillator with an fm input.  Why  cosine if
it produces sine waves?  Well, that's what Pete Samson calls it... It  really is
a SINE oscillator, but can be made into a cosine oscillator by setting the ANGLE
  ____
initially to π/2,  which in binary would  be 010000... (unsigned),  the quarter-
wave point.  (Kneadless to say,  such phase tweaking can be perpretrated  on any
waveform).



12-2. SAWTOOTH

For this,  the oscillator merely  has to pass  on the oscillator  angle directly
since  the  angle consists  (for  positive frequencies)  of  an  increasing ramp
function.  The wave starts at whatever  you set ANGLE to be (maybe  00000...) it
goes  from  there to  11111...,  then  wraps around  to  00000...  and continues
increasing. (It would be decreasing if the FREQUENCY were negative.) Since ANGLE
here is considered to be the actual waveform passed by the oscillator, we should
think of the ANGLE term as being a two's complement signed number since  we want
to represent the waveform as  having a positive and negative domain  as acoustic
waves do.  Remember, the number  from the oscillator side, regardless of  how it


                                    Page 29
LRNSAM                          Oscillator modes                           DRAFT


is  arrived at,  is  multiplied by  the output  of  the envelope  side  in two's
complement  form.   This  illustrates  how ANGLE  can  be  considered  signed or
unsigned depending on how it is used.  When ANGLE is used to look up values on a
sine table the sine of ANGLE =  0000... will be zero no matter how  we interpret
ANGLE.  But it must be interpreted as a two's complement number if we  intend it
to be the actual result of the oscillator.



12-3. SQUARE

The  oscillator produces  square waves  by simply  testing the  value  of ANGLE.
while  ANGLE  is  ≥  10000...(unsigned .5)  the  oscillator  produces  the value
01000...  (signed).   When ANGLE ≤  01111...(unsigned), the  oscillator produces
11000...(signed -.5).   Again, the  distinction between  signed and  unsigned is
that, here we use  ANGLE merely as an internal  counter, which in its  raw state
might as well be unsigned,  but the result of the  test must be signed as  it is
the output of the oscillator.



12-4. PULSE_TRAIN

During a  pass in which  ANGLE overflows  a pulse of  +.5 (01000...,  signed) is
produced.  Otherwise zero is returned. ANGLE wraps around (as always).



12-5. COS_FM

The oscillator output is determined by the sum of FREQUENCY (GJ) and FM.  Notice
that we are taking the cosine of  the frequency term, not the angle term,  as we
would for a normal oscillatory mode.  This is another case where terms  are used
for something  other than that  for which  they are named.  What this  mode does
basically is  just look  up whatever  it finds in  sum memory  location FM  in a
cosine table.  The addition  of the  FREQUENCY term  is peripheral.   This mode,
therefore doesn't  really oscillate as  such.  Its principal  use is  looking up
cosine values for certain filtering applications.



12-6. SUM_OF_COSINES

This allows the  addition of many equal  amplitude cosine waves together  in one
generator, rather than  having to dedicate several  to the task.  The  result of
the  sum of  many  equal amplitude  cosines  where the  frequencies  are integer
multiples  is  a  band-limited  pulse  train.  This  means  that  the  waveshape


                                    Page 30
LRNSAM                          Oscillator modes                           DRAFT


approaches that of a pulse train,  but the frequency bandwidth is no  wider than
the difference of the frequency of the highest component minus the  frequency of
the lowest.  The frequency of the highest component of a regular pulse  train is
infinite, therefore the bandwidth is also infinite.



12-7. A note on band limiting

A waveform that  has an infinite number  of partials is not  band-limited (seems
reasonable,  as tautologies  go...).   Since we  are working  in  a sampled-data
world, any partials that exceed in frequency 1/2 the sampling rate  are "folded-
over" back into the area below that limit and are included in the total spectrum
in strange and miraculous ways. The modes SQUARE, SAWTOOTH, and  PULSE_TRAIN are
all non-band limited  under all conditions.  The SUM_OF_COSINES and  COSINE mode
are band  limited but  SUM_OF_COSINES can  still cause  foldover if  its highest
frequency  component exceeds  half  the sampling  rate.  COSINE  can  also cause
foldover if its  frequency exceeds half  the sampling rate.   PULSE_TRAIN always
has terrible foldover and is not usually what you want to listen to.  SQUARE and
SAWTOOTH are  technically not band  limited, but the  amplitude of  the partials
falls off rapidly enough  so that if the frequency  is not too high,  the higher
partials are not audible.



























                                    Page 31
LRNSAM                                                                     DRAFT


                                   Section 13
                                   _______ __

                              Envelope processing
                              ________ __________



On  the envelope  side, a  12 bit  unsigned fractional  binary number  (a number
between 0 to 1) is produced which is used to scale the output of  the oscillator
by  multiplication.  Since  the result  of the  oscillator side  is  signed, the
multiplication is two quadrant.



13-1. EXPONENT and RATE
EXPONENT (GQ) stores the  current value of the  scaling number.  RATE (GP)  is a
constant which is added into EXPONENT (GQ) every pass to increment  or decrement
it thus augmenting or  diminishing the amplitude scaling number.   Taken through
time therefore, EXPONENT (GQ) produces a ramp function from 0 to 1  (unless RATE
is  exactly 0,  of course,  in which  case EXPONENT  remains constant).   If the
generator is  in one  of the  "sticky" modes,  then the  EXPONENT will  not wrap
around.  It will "stick" at  the last value attained before overflow  (which may
or may not be the largest number it can represent).



13-2. ASYMPTOTE

The  value in  ASYMPTOTE (GL)  is added  to (or  subtracted from)  the  value in
EXPONENT  and this  final number  is the  one used  to scale  the output  of the
oscillator.  The  effect of ASYMPTOTE  therefore is to  offset the the  value in
EXPONENT by some constant value.

It can be cleverly used in conjunction with sticky mode as follows: Say  you are
producing a very complex amplitude function, and you want to start  the envelope
at .16 and let it increment by some value for a few passes to .95.   Well, let's
remember  the potential  for  disaster that  can  happen if  the  command that's
supposed to  catch it at  .95 doesn't  arrive in time.  It would  appear offhand
that, since the value we want to stop at is well below overflow, we don't get to
use the sticky  envelope feature.  But!   Let's put an  initial value of  .05 in
EXPONENT, and lets put .16 - .05 = .11 in ASYMPTOTE.  The number in EXPONENT now
goes  from .05  to overflow  and will  therefore stick  if necessary,  while the
actual output of the envelope side goes from .16 to .95 as usual.

If an exponential envelope curve is required, then EXPONENT (GQ) is used to look
up a value in a table  of exponents which is then returned instead  of returning
its linear value.  The  linear value used to do  the lookup is kept,  however to
compute the subsequent table address to lookup.  The value of ASYMPTOTE is added
to (subtracted from) the value from the exponential table.

                                    Page 32
LRNSAM                        Envelope processing                          DRAFT


The result of the envelope process is multiplied by the result of the oscillator
process.  This constitutes a 2 quadrant multiply.  The final result is an 18 bit
number and is added into  sum memory right adjusted, sign extended.   This final
result is  added to whatever  is the current  value in this  pass sum  memory in
location SUM_MEMORY.

Note: when the output of a  generator is being used as the  modulating frequency
of another generator, the full 20  bit sum memory word is read into  the carrier
generator's FM port and is added with the bits in FREQUENCY left  adjusted.  But
the default output of the modulating generator is 18 bits rignt adjusted.  So we
must scale the amplitude of the modulating frequency to make it be left adjusted
in the 20 bit  word in SUM MEMORY  so its high order  bit will line up  with the
high order bit in FREQUENCY so as to add properly.  This is done  by multiplying
the amplitude of the modulating generator  by 4, which has the effect  in binary
arithmetic of left-shifting the entire  number two places.  This puts  the high-
order bit of the  modulating waveform into the  20th bit position in  SUM MEMORY
and it is left justified.
































                                    Page 33
LRNSAM                                                                     DRAFT


                                   Section 14
                                   _______ __

                                 Envelope modes
                                 ________ _____



These modes specify  the arithmetic operations  of envelope processing.   How we
use the "Definition" terms will be explaned when we talk about  actually running
the box.  Bascally, it consists of a predefined word full of bits which  acts as
a flag to Sam to enable some mode.

Definition Function
--------------------------------------------------------------------------------
LPLUSQ     Add EXPONENT (GQ) to the offset constant
           in ASYMPTOTE (GL).
LMINUSQ    Subtract EXPONENT (GQ) from offset constant
           in ASYMPTOTE(GL)
                                  -EXPONENT
LEXPLUS    Add ASYMPTOTE (GL) to 2          and scale.
                                         -EXPONENT
LEXMINUS   Subtract ASYMPTOTE (GL) from 2          and scale.

                                     Fig. 7
                                     ____ _


LPLUSQ: The value in EXPONENT is added to the constant in ASYMPTOTE.

LMINUSQ: The constant value in ASYMPTOTE (GL) is subtracted from EXPONENT (GQ).

LEXPLUS: The value in EXPONENT is  used as a lookup in an exponential  table and
the result is  added to the constant  in ASYMPTOTE.  The exponent  returned from
                                    -EXPONENT*16
the table lookup can be defined as 2            .  To see what's happening here,
first remember that  EXPONENT takes values  between 0 and  1.  If EXPONENT  is 0
                                          0
then the value from the table will be 1 (2  = 1).  If EXPONENT is 1/16, then the
                                                 -16
result is .5.  If EXPONENT is  1, the result is 2   , taken as  zero.  Inbetween
values are exponential.  We  could also look at  it in binary. The  following is
all unsigned.









                                    Page 34
LRNSAM                           Envelope modes                            DRAFT


EXPONENT:              Result from table:
binary       decimal   binary              decimal
0000 0000... 0         111 111 111 111     1
0001 0000... 1/16      011 111 111 111     1/2
0010 0000... 1/8       001 111 111 111     1/4
0011 0000...           001 111 111 111     1/8
0100 0000... 1/4       000 111 111 111     1/16
 .
 .
 .
1111 1111... 1         000 000 000 000...  0

So in the  binary representation, it  is like the EXPONENT  has a 4  bit integer
part (the high order bits) and an 8 bit fractional part(the rest of the word).

LEXMINUS: Exponential  table lookup is  done, and value  is subtracted  from the
constant.
































                                    Page 35
LRNSAM                                                                     DRAFT


                                   Section 15
                                   _______ __

                           Shape-shifting generators
                           ______________ __________



As you know, the generator parameters must do double duty sometimes depending on
whether they are acting as data paths or waveform generators.  Here we  get into
the guts of the issue.(1)

The  generators function  in a  variety  of ways  in the  box.   Their principle
functions are 1) waveform production, 2) the transfer of data from sum memory to
CPU, 3) the  transfer of data from  CPU to sum memory,  4) the transfer  of data
from sum  memory to  the DACs and  5) doing  sine table lookups  on data  in sum
memory.  All of this is  done, without having to create new  processing elements
and parameters, by making various existing generator parameters function in more
than one way.   Each generator run mode  uses different parameters  in different
ways, hence the following "road map" which gives a close (though not definitive)
look at them.

Remember that the generators have two parts, an oscillator part and  an envelope
part.   The oscillator  side of  the generator  is running  if it  is in  any of
G_WAIT, A_RUNNING, B_RUNNING C_RUNNING  and READ_DATA modes.  It is  not running
in INACTIVE, G_PAUSE, WRITE_DATA and WRITE_DAC modes.  By "running" I technicaly
mean whether ANGLE is being updated or not.  A special exception to this rule is
COS_FM where part of the oscillator is running (the sine table lookup  part) but
the rest of it is not.

So here is a  list of the generator parameters,  and a description of  what part
they play in the various run modes.



15-1. RATE

RATE (GO) is the oscillator  frequency sweep rate.  RATE stores a  number which,
when the oscillator is running, is  added to FREQUENCY (GJ) on every  pass, This
allows FREQUENCY to rise or fall at a given rate.  RATE functons as such  in all
oscillator run modes.  If the generator is in WRITE_DAC mode, then RATE  is used
to store the address of the DAC, and its value is not added into FREQUENCY.




_______________________
(1) Note, this  is not the  order of the  processing steps.  For  the definitive
processing  order,  as  well  as  all  the  gory  details,  see  the Programming
Specification.

                                    Page 36
LRNSAM                     Shape-shifting generators                       DRAFT


15-2. FREQUENCY

FREQUENCY  (GJ)  stores the  oscillator  frequency.  The  data  from  sum memory
addressed by FM is added to the value in FREQUENCY in all oscillator  modes.  FM
will  presumably  address  either  frequency  modulation  data  from  some other
generator or  will have data  to be sent  to a DAC.   If the oscillator  side is
running  then  RATE  is  also  added  to  FREQUENCY  (as  described  above, this
increments the frequency by a constant, for frequency slewing, or glissando).



15-3. ANGLE

ANGLE (GK).  The primary function of  ANGLE is to store the current  position of
the oscillator in the cycle of  the wave it is producing, called  the oscillator
angle.  This term is used as an index to lookup sine-cosine values on a table or
as the datum of  a logic test for the  straight line waveforms.  However,  it is
used this way only when the oscillator is running.

The principal exception to this is COS_FM.  In COS_FM mode, ANGLE is not used at
all. Instead, the output of the oscillator is the sine of the sum of FREQUENCY +
<contents of FM>.

If the generator run mode is  WRITE_DATA or WRITE_DAC then the data sent  to the
CPU is the word addressed by  FM.(1).  The entire table is actually  shifted one
half a position, as  it were.  This is  because the CSC taken  in SUM_OF_COSINES
mode is 1/SIN, and if we took  the sin of 0 we'd get infinity.  But  by shifting
the sine table, this is mitigated.  This shift won't screw you until you  try to
do sine summation synthesis.]



15-4. NCOSINES and SCALE

N_COSINES (GN), SCALE  (GM).  These are  relevant only for  SUM_OF_COSINES mode.
N_COSINES specifies  the number of  cosines to be  summed.  SCALE gives  a scale
factor for normalizing the summed result.  (If you have a bunch of cosine waves,
remembering that they all have an amplitude of 1 at zero degrees, their  sum can
begin  to  stack   up,  hence  the   need  for  normalization.)   Actually,  the
SUM_OF_COSINES algorithm should be normalized by 1/NCOSINES, but we don't have a
divider at that point in the processor, so we approximate it by just scaling the
result of the cosine summation formula by shifting the bits in the word right or
left, which you recall is the same  as dividing or multiplying by a power  of 2.
This gets us within a factor of 2 of the correct normalization, not perfect, but
acceptable.
_______________________
                                                                        -13
(1) Note that in the sine table there is an implicit phase shift of 2π*2

                                    Page 37
LRNSAM                     Shape-shifting generators                       DRAFT


Please note that  SCALE should be  set to zero when  the oscillator mode  is not
SUM_OF_COSINES, since a  non-zero value in SCALE  implies a subtraction  of one.
(This is necessary for the SUM_OF_COSINES algorithm to get rid of the cos(theta)
term).  Note that this also  means that in SUM_OF_COSINES mode, SCALE  should be
non-zero.(1)



15-5. FM

FM (GFM) has  the address of  a location in sum  memory which the  generator can
read.  FM can read from either generator or modifier last pass.  FM is  read and
its value is added together with FREQUENCY in all oscillator modes.  Please note
that if you don't want data from sum memory then you must give FM the address of
some location in sum memory which is never written into and will hence always be
zero.



15-6. SUM_MEMORY

SUM_MEMORY (GSUM) has  the address of  a location in  sum memory into  which the
generator writes  its output.  This  output is generally  added to  the contents
already there from this pass.  But if the run mode is READ_DATA, then sum memory
gets the final output of the generator PLUS the data read from the main computer
CPU.  This REPLACES the contents of the sum memory location addressed (so its no
longer really sum memory).



15-7. RATE EXPONENT and ASYMPTOTE

RATE  (GP),  EXPONENT  (GQ)  and  ASYMPTOTE  (GL)  are  relevant  only  when the
oscillator is in a wave producing mode and the envelope is running as  well. The
envelope side is running  in A_RUNNING, B_RUNNING and C_RUNNING  only.  EXPONENT
is  the  envelope  increment  corresponding  in  function  to  FREQUENCY  in the
oscillator.  It  can be  augmented or  diminished by  a number  in RATE  just as
FREQUENCY can be changed by SWEEP.  EXPONENT is either used directly as a linear
function or  as an  index to an  exponential table  used to  produce exponential
envelopes.   The  resulting  number  returned  from  either  path  is  added  to
ASYMPTOTE, which is a constant used to offset the envelope ramp from the X axis.



_______________________
(1) For  a discussion of  sine summation synthesis  see Godfrey  Winham, Kenneth
Steiglitz, "Input Generators for Digital Sound Synthesis", J. Acoust.  Soc. Am.,
                                                           __ _______  ____ ___
Volume 47, #2 (part 2) 1970, pp665-666.

                                    Page 38
LRNSAM                                                                     DRAFT


                                   Section 16
                                   _______ __

                                 Magic numbers
                                 _____ _______



Specifying frequency and duration values to the box needs a  little explanation:
We know that MUSCMP requires scaling  by a value called MAG.  (MAG is  the ratio
of  the waveform  table length  to the  clock rate,  or in  a  typical instance:
512/12800 = .04). For instance, if a variable P3 is used to scale  the frequency
of an oscillator and it has  a frequency value in Hertz, the scaled  number sent
to the MUSCMP oscillator would be MAG*P3.

In the box, FREQUENCY (GJ),  the oscillator frequency parameters must  be scaled
similarly to the way the frequency in MUSCMP is scaled by MAG.  The magic number
                                        28
which corresponds to MAG in the box is 2  /sampling_rate.

To see this, remember that FREQUENCY (which is 28 bits long) is really  a number
that is added into the number  in ANGLE every pass.  As ANGLE goes  from 0000...
to 1111...  we go through a  complete cycle of our waveform.  Consider  the case
where we have all ones in  FREQUENCY.  What happens to ANGLE every pass?   It is
wrapped  around exactly  back to  where it  was last  time.  Thus  the frequency
produced is  logically equal  to the sampling  rate since  ANGLE goes  through a
complete revolution every pass. (Though in fact the samples that are  seen don't
reflect  this, since  ANGLE always  wraps around  to the  same place  they never
change value.) Similarly, if FREQUENCY  contains all zeros, the number  in ANGLE
will not change, and the frequency of the generator is equal to zero  (though by
inspection the values produced  are indistinguishable from the above  case where
the frequency is equal to the sampling rate).  This means that if the  number in
FREQUENCY is 100000... (unsigned), then  we are producing a frequency of  1/2 of
the sampling rate, 010000... is 1/4 of the sampling rate, &etc.  So you  can see
that the frequency of a generator is a function of the speed at  which FREQUENCY
makes ANGLE go through a complete  cycle, and this in turn is controlled  by how
many passes are accomplished (and therefore samples produced) per second.

For a concrete example, say we had a sampling rate of 12800 samples  per second.
                                 28           28   7         21
The magic number  would then be 2  /12800  = 2  /(2 *100) = 2  /100  = 20971.52.
This number multiplied by some  frequency will put the correct binary  number in
                                                             6
FREQUENCY.  For instance, 1/2 the sampling rate = 6400 Hz = 2 *100.

                    21
                   2
         6                 27
        2 *100 * ------ = 2  ,                                            Equ. 3
                   100

                                    Page 39
LRNSAM                           Magic numbers                             DRAFT


which in a 28 bit binary word is 10000... as in our example above.

"But how", the alert reader asks, "can you [the author] say that if FREQUENCY is
all ones,  that it turns  ANGLE over  exactly once since  FREQUENCY has  28 bits
whereas ANGLE has only 20?  It  should, by rights, turn it over 8  bits farther,
no?" Elementary,  actually...  You see,  dear fellow, the  low order  eight bits
(the rightmost bits) of FREQUENCY are  not added into ANGLE at all,  rather, the
high order 20 bits of FREQUENCY are  all that are added to ANGLE.  So  what good
are the  low order 8  bits?  It's  more cleverness on  Pete Samson's  part.  The
point is that we want  to try to get as  fine a change of frequency  as possible
when FREQUENCY is being incremented by SWEEP so that we can  represent extremely
slow changes  in frequency.  To  this end, SWEEP  is added into  FREQUENCY right
adjusted, meaning its 20 bits are added to the righthand, or low order,  20 bits
of FREQUENCY.  But since only the left  20 bits of FREQUENCY are used to  add to
ANGLE,  the  right  eight bits  act  as  an accumulator  of  subtler  changes of
frequency than can be represented by  20 bits.  When the eight bits  finally sum
up enough to toggle  the 9th bit, then the  amount is registered as part  of the
new 20 bit frequency  increment sent to ANGLE.  Thus  we get eight more  bits of
frequency  precision with  respect to  glissando, without  having to  go  to the
expense of making ANGLE lookup in a 28 bit sine table.

One further word of interest: FREQUENCY  is a 28 bit word, but the  largest word
that can be loaded in one update  tick is 20 bits.  So Samson has given  you the
option of loading just 20  bits or all 28 by  taking two update ticks to  do it.
All this is done automatically by the software.  If the number you want  sent is
less than or equal to 20 bits it loads only that many.  But if this is the case,
then FREQUENCY acts like  a 20 bit word (the  sign bit is extended).   The magic
                     20
number then becomes 2  /clock_rate.

All processing that results in output  is in terms of passes.  This is  the same
as MUSCMP, where each output sample is derived from one "pass" through  the code
which comprises the instrument.  One  difference is the treatment in  scaling of
the  duration  of envelopes.   In  MUSCMP, envelopes  are  usually  generated by
oscillators, which means that if a  variable P2 has a value of absolute  time in
it, the scaled number  sent to the oscillator  would be MAG/P2.  However  in the
box, the scaling is simpler.  Since the envelope side of the  generators returns
a new envelope value on every pass, we only need to let the envelope run as many
passes as it would take to get  the value we want: so the number of  passes that
the  envelope would  be allowed  to process  would be  P2*clock_rate.   We would
increment the size of  the envelope by P2/clock_rate  on every pass to  obtain a
final value of 1 at the end of P2*clock_rate passes.

Another  useful  formula is  for  SWEEP, the  frequency  sweep rate.   To  get a
frequency sweep from Frequency_1  to Frequency_2 in one second,  put Frequency_1
times  the  box's  magic  number  in  FREQUENCY  (GJ),  and  the  difference  of



                                    Page 40
LRNSAM                           Magic numbers                             DRAFT


                                                       28           2
Frequency_2 - Frequency_1 times another magic number, 2  /clock_rate , in SWEEP.
That would be:


                         28
FREQUENCY ← Frequency_1*2  /clock_rate,                                   Equ. 4

and

                                     28           2
SWEEP ← (Frequency_2 - Frequency_1)*2  /clock_rate .                      Equ. 5

The derivation of the formula for SWEEP is as follows:
                     28                             28
       (Frequency_2*2  /clock_rate) - (Frequency_1*2  /clock_rate)
SWEEP ← ------------------------------------------------------,
                                clock_rate



                                         28
            (Frequency_2 - Frequency_1)*2  /clock_rate
SWEEP ←     ---------------------------------------------,
                            clock_rate



                                       28
          (Frequency_2 - Frequency_1)*2  *1/clock_rate
SWEEP ←   -----------------------------------------------,
                            clock_rate



                                     28
SWEEP ← (Frequency_2 - Frequency_1)*2  *1/clock_rate*1/clock_rate,



                                     28           2
SWEEP ← (Frequency_2 - Frequency_1)*2  /clock_rate .                      Equ. 6



To make this sweep happen in some other than one second, simply divide the above
number by the desired duration.


                                    Page 41
LRNSAM                                                                     DRAFT


                                   Section 17
                                   _______ __

                                   Modifiers
                                   _________





17-1. Modifier parameters

Modifiers do all kinds of things.  Again, what you have them do determines which
of the following terms are relevant.  They have the following locations:

Id    Size Definition          Function
--------------------------------------------------------------------------------
M0    30   COEFF0              coefficient
M1    30   COEFF1              other coefficient
L0    20   TERM_0              running term
L1    20   TERM_1              other running term
MIN   8    A_IN                read address in sum memory
MRM   8    B_IN                other read address
MSUM  7    SUM_MEMORY          write address in
                               sum memory
           ADD_SUM_MEMORY      same as SUM_MEMORY.
           REPLACE_SUM_MEMORY  also has write addr.
                               but result replaces sum mem. value
MMODE 9    MODE                run mode of modifier
                               and scale factor for the multiplies
              A_SCALE          scales COEFF1. (That's right A↔1!?!)
              B_SCALE          scales COEFF0.
FUNCTION                        same as MODE.

                                     Fig. 8
                                     ____ _




17-2. Running terms

TERM_0  and  TERM_1  are  "temporary"  locations  dedicated  to  that particular
modifier which will have as their contents whatever was last stuffed  into them.
The machine never resets them to zero as it does to sum memory.  The main use of
the  running  terms is  in  certain  of the  modifier  procedures  which require
information to be carried over from the calculations performed on the last pass.
Filtering is an example of this, as is random number generation.   These running
terms are also used when there is not enough time during one tick to do  all the
computations, so a temporary location is needed to hold the partial results over


                                    Page 42
LRNSAM                             Modifiers                               DRAFT


to the  next pass.   The running  terms live  inside their  particular modifier.
They are not cleared on every pass as is sum memory.  They always have  the last
value that was written into them,  and they divulge it only to  their associated
modifiers. You can, of  course initialize them by  using an update tick  to load
them, which you would want to do, for instance, for random number generaton.



17-3. Coefficient terms and Read terms

COEFF0 AND  COEFF1 retain  the number  deposited in  them across  passes thereby
acting as constants.  Their usual function is to scale the running terms  or the
read terms, A_IN and  B_IN.  The read terms,  as in the generators,  contain the
address of a location in sum memory, and all arithmetic operations with them act
on the value of the number in sum memory that they address.



17-4. Scaling terms

The product of a coefficient term by either a running term or a read term can be
scaled by  A_SCALE and B_SCALE  to make it  bigger.  Making the  product smaller
                                                                         _______
than one is  simple since normally the  multiplies will always be  in fractional
                          ________
range (-1.0000... to +.9999...).  (The exception is integer mixing, discussed on
page 46) Scaling is the only way to make the result of the multiply  bigger than
one.  Scaling is done by left-shifting (LSH) the multiplicand by one,  two three
or  four places.   The  effect this  has is  to  raise the  multiplicand  by the
following respective powers  of 2: 1,  2, 4, 8.  To  see this, take  binary 0010
which is decimal 2, and LSH it by 2 producing 1000 = 8.  Notice that the scaling
terms are put under MODE.  It worked out that there was enough room left  in the
MODE command to accommodate this extra baggage.  So you set the SCALE terms with
the power of 2 you  want, and that information is  tacked on to the rest  of the
MODE command and shipped  off to the box.   Please note that A_SCALE  scales any
multiplication done  with COEFF1, while  B_SCALE scales any  multiplication done
                               _
with COEFF0.
          _



17-5. Mode

This defines the  algorithm that the modifier  will use.  An alternate  word for
this  is FUNCTION  since for  modifiers that  we are  selecting is  a particular
function or algorithm or process, whereas the word MODE might better be used for
setting the mode of  an oscillator.  But the  two terms are equivalent,  and the
SAIL  procedures  that handle  them  know whether  the  MODE selected  is  for a
modifier  or generator.   For a  complete account  of the  various  flavors, see
section 18.


                                    Page 43
LRNSAM                             Modifiers                               DRAFT


17-6. Sum memory

Called the  same as  in the  generators, but  the assembler  knows which  one is
specified  by  context.  There  are  three  terms:  SUM_MEMORY  is  the  same as
ADD_SUM_MEMORY and for  both, the result of  the modifier procedure is  added to
the preexisting  contents (if any)  in the sum  memory location  addressed.  But
modifiers also can have their output replace the contents of sum  memory, hence:
REPLACE_SUM_MEMORY.



17-7. Initializing

It is important to note that  the first time you call a modifier  procedure that
uses  any of  these terms,  especially the  running terms,  they will  have some
leftover numbers in them that will not be meaningful in terms of your  new data,
but that  will no  doubt affect  how that  data is  subsequently handled  by the
modifier.  Therefore it is first necessary to initialize all of  these locations
to  some meaningful  value.  This  goes  as well  for the  generators  and delay
memory.





























                                    Page 44
LRNSAM                                                                     DRAFT


                                   Section 18
                                   _______ __

                              Modifier procedures
                              ________ __________



The name of the process is followed by its reserved definition followed  in turn
by an explanation.  Unless otherwise stated, the computation is in 20  bit two's
compliment, fixed binary  point notation.  Fixed  binary point is  a "fractional
notation" in that it represents only fractional numbers.  Decimal representation
of the binary range is from -1.000... to +.999...



18-1. Inactive

M_INACTIVE

Dead  to the  world.  As  with  generators, being  inactive does  not  clear its
memory.  The  parameters (COEFF0 and  COEFF1 and such)  will be set  to whatever
they were  before the  modifier went inactive.   It will  also still  occupy its
place in the ordered processing of modifiers and use up two ticks.



18-2. Mixing

MIXING

Result ← COEFF0*A_IN + COEFF1*B_IN.

Signals in A_IN  and B_IN are multiplied  by COEFF0 and COEFF1  respectively and
added together.   The COEFF terms  act here  and in many  other of  the modifier
procedures as gain factors for the weighting of the respective input terms.  The
result is placed back in sum memory location referenced by MSUM.  A_IN  and B_IN
are locations which contain addresses of numbers in sum memory whose  values are
looked up for these calculations.  A_IN and B_IN will usually be used  to lookup
sum memory locatons which  will usually contain current instantanious  values of
waveforms being created by generators (or whatever).  A_IN and B_IN can  also be
values that are the  result of modifier processing  which are then picked  up by
other modifiers, etc.  The  multiplications are in fractional form  as discribed
above.







                                    Page 45
LRNSAM                        Modifier procedures                          DRAFT


18-3. Integer mixing

INT_MIXING

Result(integer) ← COEFF0*A_IN + COEFF1*B_IN.

Computation  is the  same as  for fractional  mixing above  except  that integer
multiplying  is done.   The difference  is as  follows: In  fractional notation,
since  all numbers  are considered  to be  between .999...   and  -1.000.... the
result of  a fractional multiplication  will never be  greater than  this range.
That is, if you multiply the largest fixed binary point number you can represent
by another  one equally large,  the result  is still that  number.  There  is no
overflow.  For  instance, if you  have a  number 5 bits  long, and  you multiply
fixed binary  point numbers .11111  by .11111 you  get .11111.  However,  if you
multiply binary integer 00010.  by 00010.   you get 00100.  You can see  that if
you kept this up, you would get a result that is larger and  larger: 00100*00100
=  10000,  etc.   (Eventually  you would  get  overflow,  which  means  that the
significant bits are to the left  of the left-most place for them and  the value
is therefore greater than the space available for it.) So, now that  you've read
all this,  the point  is only  that integer  mixing allows  you to  make numbers
bigger while multiplying, while in fractional mixing above, the numbers only get
smaller.  The other way to do this is of course to use the SCALE terms discussed
above.



18-4. Latch

LATCH

Result ← TERM_1.  If COEFF1*B_IN ≠ 0 then TERM_1←A_IN.

The modifier gives as its result whatever was in TERM_1 from last pass.  Then it
looks to  see if it  should sample a  new value.  If  COEFF1*B_IN is not  0, the
condition is then  true and it  looks up the value  of A_IN and  replaces TERM_1
with it.   Otherwise, TERM_1 hangs  around until next  time.  The point  of this
function  is to  wait  for a  triggering condition  to  be true,  then  sample a
changing variable being stored in sum memory (like to look at a sine  wave being
fed into sum  memory) and then  to return the  value sampled.  If  the condition
becomes not true then  the value of the  last sample taken before  the condition
became false  is returned.   This value  will be  returned repeatedly  until the
condition is true again at which  time a new sample will be taken  and returned.
(Since a running term is used, it is necessary to clear it first.)






                                    Page 46
LRNSAM                        Modifier procedures                          DRAFT


18-5. Signum

SIGNUM

If COEFF0*A_IN < COEFF1*B_IN then the result ← -1 (integer)
else if the multiplicands are equal then the result ← 0
else if COEFF0*A_IN > COEFF1*B_IN then the result ← 1 (integer).

This tests the sign of the expression.  It is similar to the  Fortran "numerical
if" statement in that it allows one of three subsequent forks to be taken as the
result of an expression being >, =, or < 0.  The result is in integer form.



18-6. Zero-crossing pulser

ZERO_CROSSING_PULSER

If (B_IN*COEFF0)*(TERM_1*COEFF1) ≤ 0 then result ← - 1
(all bits on) else result ← 0. TERM_1 ← B_IN*COEFF0.

This  one figures  out waveform  zero crossings.   It looks  up  two consecutive
points in the waveform time  domain and asks whether their product  is negative.
That is, if you have two samples  in a waveform where the first is  negative and
the second  is positive, or  vice versa, your  waveform must have  crossed zero.
Hence the two samples surrounding  a waveform zero crossing will  be represented
by numbers of  alternate signs.  The product  of such numbers is  negative while
the  product of  numbers of  homogenous  sign is  positive.  If  the  product is
negative (a zero crossing was detected)  a -1 (all bits on) is returned,  else 0
(all  bits off).   (Another  possibility is  if one  of  the two  values  of the
waveform is 0 e.g. if values were 1 and 0.  The product would be 0, so  there is
a trap for this also.)



18-7. Minimum

MINIMUM The lesser of A_IN*COEFF0 or B_IN*COEFF1 is returned.



18-8. Maximum

MAXIMUM The greater of A_IN*COEFF0 or B_IN*COEFF1 is returned.





                                    Page 47
LRNSAM                        Modifier procedures                          DRAFT


18-9. Amplitude modulation

AMPLITUDE_MODULATION

Result ← TERM_1*COEFF1.  TERM_1 ← A_IN*((B_IN+1)/2).

This basically multiplies two waveforms together such that B_IN is used to scale
the amplitude of A_IN.  This is  done by multiplying the number to be  scaled by
another which has values between 0 and 1.  If this scaling is done through time,
the overall result  is of the amplitude  of A_IN being dynamically  modulated by
B_IN.  To do this, we first need to scrunch B_IN so its value lies between 0 and
1.  But  B_IN is  a binary  point number  which has  values between  .999... and
-1.000...  So to convert B_IN, first add 1 to B_IN, to make its range go  from 0
to 2.  Then divide  that by 2 to  make it go between  0 and 1.  Now  we multiply
A_IN*B_IN and return the product.   This process is repeated for every  point on
the waveforms A_IN and B_IN.  The steps the box takes to do this are as follows.
The value TERM_1*COEFF1  is returned where TERM_1  was determined the  last time
around  to  be  A_IN*((B_IN+1)/2).   This  process  constitutes  a  two quadrant
multiply in that the sign of A_IN is significant but that of B_IN is  not (B_IN,
being scaled between 0  and 1 will always be  positive).  So, if we  plotted the
point (A_IN,B_IN) on a graph, any  product of A_IN and B_IN would only  need two
quadrants to be represented.



18-10. Four-quadrant-multiply

FOUR_QUAD_MULTIPLY

Result ← TERM_1*COEFF1.  TERM_1 ← A_IN*B_IN.

TERM_1 scaled by COEFF1 is returned.  A new TERM_1 is then computed as A_IN*B_IN
and stored in TERM_1  to be multiplied by  COEFF1 next time around.   Here, both
the signs of A_IN and B_IN are  significant, and the product could be in  any of
the four Cartesian quadrants.  For synthesizer people, this is the ultimate ring
modulator.



18-11. Uniform noise

U_NOISE

TERM_1 ← Result ← COEFF0*TERM_1 + TERM_0.

We know that if we multiply integers, sooner or later, if we don't stop, we will
overflow, and the significant digits or bits of the product will be lost off the


                                    Page 48
LRNSAM                        Modifier procedures                          DRAFT


left edge  of our remaining  number.  That leaves  only insignificant  ones!  So
these low order, insignificant bits are then returned as a pseudo-random number.
These numbers taken over a period of time produce white, or uniform  noise.  The
procedure adds TERM_0  to the integer product  of TERM_1*COEFF0 and  returns the
result.   Overflow  is purposefully  ignored.   It overwrites  TERM_1  with this
result, to be used  next time around.  The one  tricky aspect of this  method is
that the numbers used  to prime the procedure must  be chosen with care  in this
system. Andy Moorer has the following remarks about the process:

        To make a modifier make pseudo-random numbers, you have  to choose
      COEFF0  (the  multiplier),  TERM_0  (additive  constant)  and TERM_1
      (starting random number).  Knuth(1) has a whole chapter on this sort
      of thing.   Probably the  best you can  do for  these things  is the
      following:

        1) COEFF0 is 30 bits long, but only the upper 20 bits are  used in
      the multiply. This being the case, it can be a 20 bit  number chosen
      essentially at random, but it ought to have some middle bits  on and
      it ought to  end in 101 (for  maximum potency). An example  is this:
      00000100110100100101   (octal   46445).    (Note   that   since  the
      coefficient is really 30 bits long, when we pass the number  as full
      word data we have to stick  ten more zeros on, which gives  us octal
      115112000.)

        2) This being the case,  TERM_0 should be set to zero  (unless you
      know  what you  are  doing). Setting  it non-zero  doesn't  make the
      number any more "random".

        3) The starting random number should be set to something non-zero,
      like the date  or time, your telephone  number, pi, or  whatever. It
      should be a whole word full of bits.



18-12. Triggered uniform noise

TR_U_NOISE

Result ← COEFF0*TERM_1 + TERM_0.  If B_IN*COEFF1 ≠ 0 then
TERM_1←result.

A new noise  value is calculated  as in U_NOISE  above only when  the "trigger",
B_IN*COEFF1,  is  not 0.   This  allows the  production  of noise  values  to be
dependent on some  state in the  box rather than requiring  a command to  get it
going or stoped, thereby lightening the command stream.  Priming of the terms of
this procedure should be done according to the rules for U_NOISE above.
_______________________
(1) Knuth, The Art of Computer Programming, Vol. II.
           ___ ___ __ ________ ___________

                                    Page 49
LRNSAM                        Modifier procedures                          DRAFT


18-13. Threshold

THRESHOLD

If COEFF0*A_IN + TERM_0 < 0 then result ← 0 else if ≥ 0 then result ←
COEFF1*B_IN.

This allows  one to  detect when a  signal crosses  some threshold.   The tested
value is A_IN*COEFF0 + TERM_0.  If it is less than 0 then 0 is returned, else if
it is greater than or equal to 0 then B_IN*COEFF1 is returned.   B_IN*COEFF1 has
nothing to  do with  the value  of the tested  expression but  could be  made to
evaluate to a  constant acting as  a trigger to some  other process or  since we
have B_IN in the  term it could be some  other waveform which would  then become
the output of the modifier.



18-14. Invoke delay unit

INVOKE_DELAY_UNIT

(B_IN(low order 5 bits) ← Unit#.)
result ← TERM_0 + TERM_1*COEFF0;
TERM_0 ← <delay_memory_value from delay unit>;
TEMP0 ← A_IN + <delay_memory_value>*COEFF1;
(<wait for next pass>)
TERM_1 ← TEMP0;
(<wait for next pass>)
<value sent to delay unit> ← TEMP0;

Delay units are  called from modifiers.  The  delay units are discussed  on page
61. The modifier acts like a processor for the delay units, and the  delay units
only do the clerical work of stuffing and retrieving data.  The  processing that
the modifier does to the data sent to the delay units is as folows:

The address of the delay unit to  be accessed this pass is fetched from  the sum
memory location addressed by B_IN.  At this point, TERM_0 has the datum that was
read from delay memory three passes ago.  This is added to the product of TERM_1
and COEFF0.  TERM_1 has the datum that was read from sum memory two  passes ago.
(If you don't want this, just set COEFF0 to zero, of course.) The result of this
sum is then returned to the sum memory location addressed by SUM_MEMORY.

Next we receive a new delay memory  datum from the delay unit and deposit  it in
TERM_0.

Then we  process the data  to be  written to delay  memory.  This  takes several
passes, therefore we  must use TEMP0 which  is an internal register  accessed by


                                    Page 50
LRNSAM                        Modifier procedures                          DRAFT


the modifier that is not cleared from pass to pass.  TEMP0 is given the  sum of:
1) a word from sum memory addressed by A_IN (presumably the next sound sample to
be reverberated, etc.) and 2) the  product of the current datum from  sum memory
and COEFF1.  This takes a pass.  On  the next pass TERM_1 is given the  value in
TEMP0. On the next pass TEMP0 is passed to the delay unit to be deposited in its
memory.











































                                    Page 51
LRNSAM                                                                     DRAFT


                                   Section 19
                                   _______ __

                              Filtering algorithms
                              _________ __________



Various  flavors  of  filtering  are available.   Here  is  the  barest outline,
starting with a SAILish version of the code performed.  Section 19 has  a little
more on how to use them, written by Andy Moorer.



19-1. One pole

Called as ONE_POLE mode to a modifier.  Sam does the following loop:

    LOOP: Result[k]←COEFF1*TERM_1 + COEFF0*B_IN;
    TERM_1←Result[k];
    <increment k and go to LOOP>.

Or, the formula could be given as:

    Result[k]←COEFF1*Result[k-1] + COEFF0*B_IN[k],


where k  is the number  of the current  sample, B_IN[k] is  the location  of the
current input sample, COEFF0 and COEFF1 are gain factors.  The result  is formed
of TERM_1*COEFF1  + B_IN*COEFF0, then  TERM_1 is replaced  by the result,  to be
processed on the next pass.  In this way TERM_1 becomes the k-1'th sample.




19-2. One zero

ONE_ZERO

Result←TERM_1*COEFF1     +      A_IN*COEFF0,     then      TERM_1←A_IN.      Or:
Result[k]←A_IN[k]*COEFF0 + COEFF1*A_IN[k-1].










                                    Page 52
LRNSAM                        Filtering algorithms                         DRAFT





19-3. Two poles

TWO_POLES

    LOOP: Result[k]←COEFF1*TERM_0 + COEFF0*TERM_1 + A_IN;
    TERM_0←TERM_1;
    TERM_1←Result[k];
    <increment k and go to LOOP>.
This is like saying
    Result[k]←COEFF0*Result[k-1] + COEFF1*Result[k-2] + A_IN.



19-4. Two zeros

TWO_ZEROS

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←A_IN;   <increment   k  and   go   to  LOOP>.    This   is   like  saying
Result[k]←A_IN[k] + COEFF0*A_IN[k-1] + COEFF1*A_IN[k-2].




19-5. Two poles COEFF0 variable

TWO_0POLES

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←Result[k]; COEFF0←COEFF0 + B_IN; <increment k and go to LOOP>.




19-6. Two poles COEFF1 variable

TWO_1POLES

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←Result[k]; COEFF1←COEFF1 + B_IN; <increment k and go to LOOP>.






                                    Page 53
LRNSAM                        Filtering algorithms                         DRAFT


19-7. Two zeros COEFF0 variable

TWO_0ZEROS

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←A_IN; COEFF0←COEFF0+B_IN; <increment k and go to LOOP>.



19-8. Two zeros COEFF1 variable

TWO_1ZEROS

LOOP:   Result[k]←TERM_0*COEFF1   +   TERM_1*COEFF0   +   A_IN;   TERM_0←TERM_1;
TERM_1←A_IN; COEFF1←COEFF1+B_IN; <increment k and go to LOOP>.



19-9. A little digital filtering theory

(...never hurt  anybody) Now I  bet you want  to know how  to use  these things,
right? Well,  filters are  entirely specified by  their frequency  responses, so
let's take a look:  In what follows, w is  the radian frequency (2πf) of  a pure
sinusoid applied  to the filter  to determine its  frequency response. T  is the
sampling interval, or 1/clock_rate.

                                     2
ONE POLE: H(w) = COEFF0/sqrt(1+COEFF1 -2*COEFF1*cos(wT))                  Equ. 7
                            2       2
ONE ZERO: H(w) = sqrt(COEFF0 +COEFF1 +2*COEFF0*COEFF1*cos(wT))            Equ. 8
                                2
     or   H(w) = COEFF0*sqrt(1+R +2*R*cos(wT)) where R=COEFF1/COEFF0      Equ. 9
                                                          2
TWO POLES:H(w) = 1/sqrt((1-COEFF0*cos(wT)-COEFF1*cos(2wT))
                                                         2
                        +(COEFF0*sin(wT)+COEFF1*sin(2wT)) )              Equ. 10
                                                        2
TWO ZEROS:H(w) = sqrt((1+COEFF0*cos(wT)+COEFF1*cos(2wT))
                                                         2
                        +(COEFF0*sin(wT)+COEFF1*sin(2wT)) )              Equ. 11

These expressions won't mean much  without some explaination. Let's look  at the
ONE POLE  case. Since cos(0)=1,  H(0)=COEFF0/|(1-COEFF1)|. At half  the sampling
rate, wT=π so cos(wT)=-1, thus H(2πclock_rate/2)=COEFF0/|(1+COEFF1)|. So  we see
that the frequency response starts at H(0) and varies roughly  cosinusoidally to
H(2πclock_rate/2). If COEFF1<1, then  this means that for positive  COEFF1, this
is  a low-pass  filter (accentuates  frequencies around  zero) and  for negative


                                    Page 54
LRNSAM                        Filtering algorithms                         DRAFT


COEFF1, it is a high-pass filter (depresses frequencies around zero).  Note that
if COEFF1=1  or -1, you  get a filter  the frequency response  of which  goes to
infinity either at 0 or 2πclock_rate/2. This is not recommended.

For ONE  ZERO, it is  the same  deal except that  it is the  ratio of  COEFF1 to
COEFF0 that  sets the  frequency response  and the  sign is  inverted.  Positive
COEFF1 makes a high-pass and negative COEFF1 makes a low-pass.

TWO POLES: Now the really interesting (and hard to explain) stuff comes  when we
get to two of everything.  (double your pleasure, double your fun!...)  The only
way to deal with a  two pole or two zero filter  is to put it in  canonical form
(that word again!). Here is the canonical form for the TWO POLES case:

                                    2          2
    H(w) = G/sqrt((1-2*R*C*cos(wT)+R *cos(2wT)) +(-2*R*C*sin(wT)
       2          2
    + R *sin(2wT)) )                                                     Equ. 12

                                      2
where G=1, COEFF0=2*R*C, and COEFF1=-R .

Without further explaination, this is a resonator (formant filter). Its resonant
frequency is determined by C,  which is cos(2πfT). Its bandwidth  (sharpness) is
determined by R which must be less than one for stability. Typical  values would
be R=.95 for a fairly sharp filter and R=.999 for a filter that is so sharp that
it will "sing" at its resonant frequency almost by itself. To use such a filter,
you  choose the  resonant frequency,  f, choose  the bandwidth,  R,  the compute
                                  2
COEFF0=2*R*cos(2πfT) and COEFF1=-R . This will give you that filter.

We have, in this description, sidestepped the issue of scaling. The gain of this
filter at resonance is

                                        2      3       4
    H(2πf) = G/sqrt[4(1-cos(2πfT))((1-R) -(1-R) )+(1-R) ].               Equ. 13

This looks a lot better if you substitute something, like D, for the term (1-R):

                                    2  3   4
    H(2πf) = G/sqrt[4(1-cos(2πfT))(D -D )+D ].                           Equ. 14

For most purposes, you  can normalize a filter  by multiplying it by  (1-R). For
higher frequencies, you  need a smaller factor.  The only problem is  that there
aren't  any more  multiplies left  in  a modifier  (no COEFF2,  just  COEFF0 and
COEFF1).  This means that you have to scale the signal going into the  filter by
(1-R) first. Don't forget this, because otherwise you will get lots of overflow.



                                    Page 55
LRNSAM                        Filtering algorithms                         DRAFT


Note also that COEFF0 and COEFF1 are really less than 1.0. This being  the case,
he said  inquisitively, how  can we possibly  synthesize 2*R*C  which can  be as
large as 2? You do that using the scaling specified in the mode word  MMODE. The
right four bits of  the mode are the scaling  for the multiplies. You  can scale
the result of the multiplication by COEFF0 and the multiplication by COEFF1 by a
power of two.  If we call the right  4 bits of the mode AABB, then AA or  BB set
to 0 specifies no scaling, 1 means scale by factor of 2, 2 by factor of 4, and 3
by 8.  AA is the scaling for the multiply with COEFF1, BB is the scaling for the
multiply with COEFF0. This means that we can get 2RC by setting COEFF0 to RC and
setting the scaling for the multiply by COEFF0 to 1 (factor of 2).

Now normalizing the  response at resonance  to 1 may or  may not be  exactly the
right thing to do. The problem is that this only makes sense if your signal is a
narrow band signal, like  a pure sinusoid or something.   Otherwise, normalizing
by the gain at resonance  is too much attenuation.  Unnormalized gives  too much
gain  and normalizing  by the  gain  at resonance  is too  much  attenuation, so
anywhere in between (like multiplying by (1-R)) is probably  reasonable. Figures
9 and 10 show filter responses for two-pole resonators at three different center
frequencies and a number of  different bandwidths.  Figure 9 shows  a normalized
filter. Notice that the gain is now 0 dB (1.0) at the center frequency,  but for
high values of R, the peak  is really narrow.  Figure 10 shows  the unnormalized
filter responses. Note that  at high values of  R, the filter can  emphasize the
signal by as much as 40 dB.

Normalizing things  with zeros rather  than poles is  much easier.   The maximum
response for a filter with zeros occurs either at zero frequency or at  half the
sampling rate, and this  maximum is guaranteed to be  less than 4.0 (for  a real
notch filter, rather than two first-order filters multiplied  together). Thusly,
you  can  pretty much  ignore  the scaling  of  an all-zero  filter  because you
probably won't get too screwed by it. Figure 11 shows the responses  of two-zero
filters (or antiresonators, as we might call them).

And  not to  leave out  the  first order  filters, figures  12 and  13  show the
responses of one pole and one zero filters. Here the maximum and  minimum values
occur only  at zero  and at  half the sampling  rate.  In  all these  plots, the
horizontal axis represents  frequency from zero to  half the sampling  rate. You
can see  that positive  values of  R for  the one  pole case  represent low-pass
filters. The frequency  responses of all-pole filters  are just the  inverses of
those for all-zero filters. Isn't that handy?










                                    Page 56
LRNSAM                        Filtering algorithms                         DRAFT


FPIX.XGP[DOC,MUS] goes here.
















































                                    Page 57
LRNSAM                                                                     DRAFT


                                   Section 20
                                   _______ __

                                  Delay Units
                                  _____ _____



Delay units are called from modifiers by invoking the modifier in DELAY mode (in
a similar sense as invoking a modifier in, say, AMPLITUDE_MODULATION mode).  For
an example of calling a delay line see page 91.

The delay units  thus invoked have two  possible modes, table lookup,  and delay
line.  There is (yet another)  storage area that the delay units  address called
delay memory.  It  contains (in our  case) 48k of 20  bit words.  (A  full house
would contain up to 65,536 20 bit words.)

In delay line mode,  the modifier sends the delay  unit a word of data  which is
then stored in the current location  in the delay array.  The old value  in that
location is retrieved before the new word is written in.  This old word  is then
sent back  to the modifier  as the delayed  signal.  When the  array is  full of
words read from  the modifier, the  array pointer is reset  to zero and  it goes
down the line again retreiving the data in the location before writing  new data
received from the modifier into it.

Id     Definition            Function
------------------------------------------------------------------------------
P      MODE                  Delay unit mode
Z      DELAY_LENGTH          Delay line unit length
       SCALE                  or table address scale factor
Y      INDEX                 Running index of the delay array
X      BASE_ADDRESS          Base address of delay array

Modes:
       D_INACTIVE            dead
       DELAYLINE             does the processing described below
       TABLE_LOOKUP          see below
       ROUND_TABLE_LOOKUP    see below

                                    Fig. 14
                                    ____ __




20-1. Delay line mode

DELAYLINE

This mode can be SAILishly represented this way:


                                    Page 61
LRNSAM                            Delay Units                              DRAFT


for INDEX←BASE_ADDRESS step 1 until BASE_ADDRESS + DELAY_LENGTH do
    begin
        Result←TERM_0 + COEFF0*TERM_1;
        TERM_0←<delay memory data received>[INDEX];
        TERM_1←A_IN + COEFF1*<delay memory data received>[INDEX];
        <delay memory data sent>[INDEX]←TERM_1;
    end;


Please  note, that  the  delay arrays  are  not cleared  when  initiallized, and
therefore will return junk on their first pass.  Also, it requires an additional
three passes to return data from the delay unit, so the real turn around time is
DELAY_LENGTH + 3.



20-2. Table lookup mode

TABLE_LOOKUP

The data word received from the modifier is shifted to the right by DELAY_LENGTH
bits and then used to address the memory area assigned to the unit.  The word in
the addressed memory location is returned to the modifier three passes later.



20-3. Table lookup - rounded

ROUND_TABLE_LOOKUP

Data received is shifted right, rounded then used to address delay memory.


















                                    Page 62
LRNSAM                                                                     DRAFT


                                   Section 21
                                   _______ __

                                Calling the box
                                _______ ___ ___



The lowest level of access (short of writing your own assembler) is to set  up a
SAIL program  which requires  the source file  LOWER.DEF[SAM,MUS], and  the load
module  LOWER.REL[SAM,MUS].  LOWER.REL  contains the  SAIL  procedures described
below which  handle the  set up  of data  paths in  the box  and the  passing of
parameters.  LOWER.DEF contains the macro  definitions of all the terms  such as
ANGLE and TERM_1 that are in the  tables of this document, plus a slew  of other
macros for  various obscure  system hacks.  This  is also  where the  arrays are
declared for  the unit generators  and sum memory,  and where the  procedures in
LOWER.REL are formally called.

The procedures are:



21-1. GET

External simple integer procedure GET(integer id).

This procedure  does a lookup  on the processing  element lists and  returns the
number  of  the lowest  numbered  free element.   For  instance if  tweet  is an
integer, saying:  tweet←GET(id_generator) will  return the  number of  the first
free generator in  tweet. Tweet then  has a value  which consists of  flags that
specify a particular  unit generator.  Likewise:  tweet←GET(id_modifier) returns
the  number of  the  first free  modifier, and  tweet←GET(id_delay)  returns the
number of  the first free  delay.  The  terms in the  definition list  below are
loaded into  your program by  LOWER.DEF and contain  addresses in  the assembler
such that  when you  call the  assembler with one  of these  variables in  a GET
command, the assembler is  pointed to that part  of itself which deals  with the
clerical work of claiming that type of process element.

Definition              Function
--------------------------------------------------------------------------------
ID_GENERATOR            gets a generator.
ID_MODIFIER             gets a modifier.
ID_DELAY                gets a delay unit.
ID_SUM_MEMORY           gets a sum memory location.

                                    Fig. 15
                                    ____ __





                                    Page 63
LRNSAM                          Calling the box                            DRAFT


21-2. GIVE

External simple procedure GIVE(integer id).

This does  the opposite of  GET.  That is,  it frees a  unit generator  from its
associated variable. It also makes  that unit generator unknown to  the universe
until another GET is perpetrated. (However, you can't be assured of  getting the
same process element back, since  it always takes the lowest  numbered available
element.) A  sample command: GIVE(tweet).   It is not  necessary to  always GIVE
back processing elements.   All you are doing  is clearing an identifier  of its
association.  However, if your program is a dynamic allocation routine where you
may  be repatching  the box  on the  fly, if  you continued  glomming processing
elements  without releasing  any you  would soon  have more  processing elements
bound than you have processing ticks to run them with.  You would soon  drop out
of real time (how degrading!).




21-3. BIND

External simple integer procedure BIND(integer id, name, value).

This  allows  you to  associate  different elements  in  the box.  It  has three
functions: a) to associate input  data with fields in processing  elements, b)to
associate sum memory with input  and output ports of processing elements,  3) to
set the running status of processing elements. For instance:

                            28
BIND(tweet, FREQUENCY, 440*2  /clock_rate)

sets the generator referenced  by the bits in tweet  to have a frequency  of 440
                       28
(That is, it puts 440*2  /clock_rate in FREQUENCY (GJ)).  This is an  example of
using  BIND to  associate data  with  a processing  element.  BIND  is  also the
procedure to associate  the output and  input addresses of  processing elements.
For instance, the following sequence will link two generators:

BIND(gen_sum,SUM_MEMORY,tweedle)
BIND(didi,FM,gen_sum)

This will give the address which is in location SUM_MEMORY in tweedle  (which is
the  address of  the sum  memory location  where tweedle  writes its  output) to
location FM in  didi.  When didi  reads that address  it will get  the deposited
output of  tweedle.  An example  of setting the  running status of  a processing
element might be: BIND(fut,MODE,A_RUNNING).  Valid constructs for integer <name>
are all the generator, modifier and delay line SAIL definitons  discussed above,
such as FREQUENCY, RATE, EXPONENT, MODE, A_IN, COEFF1, etc.

                                    Page 64
LRNSAM                          Calling the box                            DRAFT


21-4. SET_OUTPUT

External simple procedure SET_OUTPUT(integer sail_channel).

SET_OUTPUT lets the user pass the  assembler command output not only to  the box
but to any number of  devices.  You must use this to  set up a path to  the box,
just as you must open any device before doing I/O to it.  But, you can also, for
instance, open the disk  and enter a file, and  pass the disk channel  number to
the assembler  with a SET_OUTPUT  and the assembler  will also pass  the command
stream to the opened file, thereby giving you a record of it.



21-5. SET_CHANNEL

External simple procedure SET_CHANNEL(integer data_chan,sail_chan).

There are "data"  channels between the  main computer and the  box as well  as a
"command" channel.  SET_CHANNEL allows the user to set a channel for  data input
                                                                      ____
and output to the box. This is  how the disk and the ADC (if  implemented) would
get data into the box.  Also, this  is how data would be forwarded from  the box
to the main computer.   This is not used for  the DACs, which have  another hook
into the box (they would be used  in conjunction with a generator set up  with a
BIND command to run mode WRITE_DAC,  see page 24).  The main use of  reading and
writing samples to and from the operating system would be to do  manipulation of
sound files.  However, this means you must wait for disk ops, and since the disk
is the current  bottleneck on the  system, it probably  means that this  will be
basically a non-real time mode.  The following are the available  device channel
definitions:

Definition    Function
--------------------------------------------------------------------------------
DISK_OUT      output to the disk.
DISK_IN       input from the disk.
ADC_IN        input from the adc. (not likely soon).
*_IN          add your favorite device here.

                                    Fig. 16
                                    ____ __




21-6. SET_PROCEDURE

External simple procedure SET_PROCEDURE(reference procedure <name>).

SET_PROCEDURE  allows  the user  to  choose  a home  grown  procedure  for error


                                    Page 65
LRNSAM                          Calling the box                            DRAFT


recovery.  LOWER.REL contains  a slew of error  traps.  The error  handler first
stops execution then, assuming you have declared an error procedure of  your own
with SET_PROCEDURE, it passes control  to your procedure and passes a  SAIL type
string to it containing a thumbnail characterization of your problem.   What you
probably want your  error procedure to  do is to save  the current state  of the
world, close files that were open and generally make a controlled  retreat.  You
DON'T want it  to try to  continue execution, since things  aren't set up  to do
that.  At this stage of things, all errors are fatal.  An instance:

simple procedure BOX_ERROR(reference string errmsg);
  begin
    print('11&"You have done the following no-no:"
        "&errmsg&'15&'12);
    close(input_file);
    close(output_file);
    release(input_file);
    release(output_file);
    usererr(0,0,"Fatal low level error...");
  end;
SET_PROCEDURE(box_error);




21-7. DECODE


external simple integer procedure DECODE(integer unit).

When passed an id that has been GETed, DECODE returns the type of unit, numbered
as follows:

ID_GENERATOR   0
ID_MODIFIER    1
ID_DELAY       2
ID_SUM_MEMORY  3



21-8. RELATIVE


External simple integer procedure RELATIVE(integer unit).

When passed an id that has been GETed, RELATIVE returns the number of  that unit
relative to the base address of that type unit.



                                    Page 66
LRNSAM                          Calling the box                            DRAFT


21-9. SET_MODE

External simple procedure SET_MODE(integer name).

This procedure is used to set various states of the whole box, such as number of
ticks per pass, etc.

Code   Definition                Function
--------------------------------------------------------------------------------
CONO-A RESET_TICK_COUNTER        to beginning of pass (if stopped).
CONO-A PERMIT_PROCESSING_TICKS
CONO-A INHIBIT_PROCESSING_TICKS  only update ticks are executed.
CONO-A RESET                     master reset.
MISC   WAIT_CLEAR                start up all waiting process elements.
MISC   PAUSE_CLEAR               start up all pausing process elements.
MISC   STOP                      (screeech...)
       OPTIMIZE                  pack as many commands per word as possible
                                 (possibly dangerous: see below).
       NON_OPTIMIZE              pack one command per word,
                                 but maybe don't run so efficiently.

                                    Fig. 17
                                    ____ __


Example: SET_MODE(PAUSE_CLEAR).

OPTIMIZE mode is potentially dangerous because in optimizing, some  commands may
get  rearranged  in time  in  a possibly  fatal  way.  Mark  Kahrs  supplies the
following example.  Suppose you want to change several commands, like:

        bind(i1,mode,5);
        bind(i1,fm,37);
        ...
        bind(i1,sum_memory,sum_outa);

Providing there are no dwells...  (Because a dwell causes the packing  tables to
be cleared), then the sum_memory change will occur RIGHT after the  mode change,
i.e., slightly out of time  sequence However, the chances of such  an horrendous
thing happening are not too likely (unless you write a really long drone piece).
There will be few cases where the micro-ordering will be that crucial.



21-10. SET_FIELD

External simple procedure SET_FIELD(integer name,field).

Use this to set the quantities of the various things described below:

                                    Page 67
LRNSAM                          Calling the box                            DRAFT


Id     Definition            Function
--------------------------------------------------------------------------------
CONO-A CONTROL_MODE          called only for three definitions below.
             C_START         start the box.
             C_TICK          cause one tick.
             C_STOP          stop the box. This command is identical to
                             STOP above, except that this one is
                             used by the controlling
                             system for interupts.
TICKS  PROCESSING_TICKS       set how many processing
                             ticks per pass.
TICKS  TOTAL_TICKS           set how many of both kinds of
                             ticks per  pass.
                             update ticks/pass = (total ticks
                             - processing ticks) / pass.
TIMER  DWELL                 process no more update ticks until
                             pass counter = data.
TIMER  SET_PASSES            set pass counter to data.
TIMER  CLEAR_PASSES_DWELL    clear pass counter then
                             DWELL until pass
                             number equals argument.
       SIZE_BUFFER           controls size of command buffer between
                             the controlling program and the box.
                             Initialized to 1024.
       SIZE_COMMANDS         Sets how many commands will be sent
                             to the buffer at once.
                             Initialized to 64.
       PACKING_MODE          called only for three definitions below:
             RIGHT_JUSTIFIED take low order 20 bits of source, pack
                             into low order end of box destination field.
             LEFT_JUSTIFIED  take low order 20 bits of source, pack
                             into high order end of box destination.
             FULL_WORD       low order 28 bits packed into destination word.

                                    Fig. 18
                                    ____ __

Example:
SET_FIELD(PROCESSING_TICKS, 300);
SET_FIELD(DWELL, CLOCK_RATE*5);
SET_FIELD(PACKING_MODE, FULL_WORD);
SET_FIELD(CONTROL_MODE,C_START);



21-11. BIND_FIELD

External simple integer procedure BIND_FIELD(integer id, name, value, type).


                                    Page 68
LRNSAM                          Calling the box                            DRAFT


This acts just like BIND above, but it has an additional field which  allows you
to temporarily (for this call only) change the packing mode.  For example,

BIND_FIELD(foo,frequency,left_justified)

will select that  packing mode for passing  frequency this time, then  revert to
whatever it was before.



21-12. Size of command buffer

There is a problem  posed by the size of  the buffering and size of  the command
block.  The command buffer is only flushed (passed to the box) when it  is full.
So, if the size of the buffer is much bigger than the buffer inside Sam, you run
the risk of command  underrun since the box's  internal buffer could run  out of
commands before the  next block is  put out.  If the  buffer is small,  there is
less likelihood of  underrun up to  a point.  The catch  here would be  that you
will need more I/O operations from the main computer system, and it may  (in its
infinite wisdom) decide not to give  them to you when you need them.   And since
you have  only a  small buffer  full of commands,  you may  run out  soon, again
resulting in underrun.   Richard Almaniac would  probably have something  to say
about this. (Don't count your ticks until they're passed?)



21-13. LOAD_DELAY

External simple procedure LOAD_DELAY(from_address,to_address,count).

This stuffs arrays into delay memory for table lookups, etc.  The data format is
that which is  set by SET_FIELD, see  above. Notice that delay  memory locations
are not arbitrarily associated with  specific delay units, hence any  delay unit
is free to read any (contiguous) part of delay memory.



21-14. INITIALIZE

External simple procedure INITIALIZE.

Initializes the code (useful in case of a restart).







                                    Page 69
LRNSAM                          Calling the box                            DRAFT


21-15. FLUSH

External simple procedure FLUSH.

FLUSH should be called before exiting the user program.  It forces data  left in
all buffers to be written out.  Failure to do so results in missing words at the
end.  So please, FLUSH when you're through... and wash your hands.



21-16. Processing element arrays

This next bit is a list  of the arrays that are declared  in LOWER.DEF[SAM,MUS].
They are modified SAIL type arrays.  The purpose of these arrays is to  keep tab
of  the processing  elements and  memory locations  in use.   When  a processing
element is in any mode other than inactive there will be a non-zero value in its
corresponding array location.  Notice that there is one big array from 0:671 and
several smaller ones  that add up  to a total  of 672.  Actually,  they overlap,
such that  the location  referenced by  unit_generators[0] is  the same  as that
addressed by  generators[0], and  location unit_generators[256]  is the  same as
modifiers[0], etc.  The point of including these arrays is so that you can write
your own procedures to do the  book keeping for the process elements  instead of
using  GET and  friends.   Also notice  that  all_sum_memory[0] is  the  same as
gen_sum_memory[0], and all_sum_memory[128] is mod_sum_memory[0].  The arrays are
allocated as follows:

--------------------------------------------------------------------------------
External integer array unit_generators[0:671].
   All the generators, modifiers
   delay units and sum memory addresses are listed here
   as to availability.

External integer array generators[0:255]. Just the generators.

External integer array modifiers[0:127]. Just the modifiers.

External integer array gen_sum_memory[0:127].
   Just the generator sum memory.

External integer array mod_sum_memory[0:127].
   Just the modifier sum memory.

External integer array all_sum_memory[0:255]. Both sum memories.

External integer array delays[0:31]. Just delay units.

                                    Fig. 19
                                    ____ __


                                    Page 70
LRNSAM                          Calling the box                            DRAFT


21-17. Tick counters

The next two variables  keep track of the  current update tick, and  the current
pass number.

external integer update_tick.

external integer pass.



21-18. Steps in calling the box

There are some important  things to realize about  the order in which  modes and
fields are set in the box.  Most obviously, you must set or clear all  fields in
the box before you crank it up.  Any processing chains must be started up in the
order that they reference each other.  Also, if modifiers are reading  from this
pass sum memory, the writing processing element must have done its  thing before
the reading modifier  (meaning the writing element  must be lower  numbered than
the reader).  So a typical program will do the following in roughly this order:
    -Require LOWER.DEF source_file and LOWER.REL load_module,
    -Do a SET_OUTPUT to pass a command channel to the assembler,
    -GET the desired processing elements,
    -Including generators to output samples to the DAC, if desired
    -Calculate the number of ticks needed, pass that with SET_FIELD
        to the assembler,
    -Set the corresponding sample rate, and magic numbers,
    -Set all unused processing element fields to reasonable values
        with BIND,
    -Set any unused FM ports in generators or A_IN type ports in
        modifiers to some unused (hence 0) sum memory location
        with GET and BIND,
    -Set all used fields to their desired values with BIND,
    -Connect processing elements to sum memory
        (and hence to each other),
    -Start the processing elements in order of reference
        (i.e., start the generator passing samples
        to the DAC last),
    -DWELL until the event requires more update ticks,
    -or if it's finished, GIVE back the used process elements
        and sum memory locations when no longer needed so other
        processors can get high-ordered time slots, keeping you in
        real time.
    -lastly, FLUSH!





                                    Page 71
LRNSAM                                                                     DRAFT


                                   Appendix A
                                   ________ _

                         An introduction to pipelining
                         __ ____________ __ __________



If we used a general purpose computer to do the processing for us,  depending on
which kind we used and how busy it was otherwise, we might be able to accomplish
our goal, but there would  be lots of wasted motion.  General  purpose computers
are meant to be able to  handle a wide variety of kinds of  jobs.  Consequently,
there is lots of extra baggage that they carry along to allow  this flexibility.
We,  however, have  a rather  specific job  to do:  most signal  processing jobs
involve  applying  a  long  series  of  data  to  a  relatively   fixed  complex
calculation.  So the first thing to  do if we were designing a computer  for our
purposes  is to  dump as  much of  the overhead  and waste  motion  as possible.
(However, it would be nice if the  device we were to build were controlled  by a
large  general purpose  computer so  that we  could have  as flexible  a  way as
possible to run it.)

Let us take the following equation as an example of a calculation we  might want
to do with our machine.  It  produces a cosine wave when fed lots  of increasing
values of I,


   Y ← A*SIN(6.28138*(I/512) + π/2).                                     Equ. 15

Our calculator will only need to be  able to add, to multiply and divide  and to
look up values in a sine table.  It would do the calculations in stages, just as
we would by hand: it would  form intermediate results and then combine  them and
put out the result.  Such a machine, without the overhead of a general computer,
could be made to be very fast.

But this still has  some problems.  What if the  formula we want were  still too
time consuming to  calculate at the  rate of speed we  want?  What if  we wanted
lots of these formulas to be running simultaneously and added together?   How do
we expand this basic process but still keep the average computation time down?

If we just got lots of these  processors and fed each one a number we  would cut
the average compute time by  1/<the number of extra processors>.  But  we'd need
some fancy system of passing the numbers out and collecting the results.

A better approach is  to realize that, since  the calculation is broken  down by
the  processor  into  intermediate  steps, it  might  be  possible  to  have one
processor working on several values of  I at once.  To see how this  can happen,
we must  know the difference  between block structure  and pipelining.   We have
been  unconsciously  treating  the  processing of  the  formula  above  in block
structure.   In this  mode,  we take  a  number for  I,  subject it  to  all the


                                    Page 72
LRNSAM                   An introduction to pipelining                     DRAFT


intermediate computations and wait for a final value for Y to pop out  before we
take another value for I.  But what if we did it this way:

Lets make an "assembly line" out  of the process.  A belt carries in  the values
of I, lets say  we have 5 values.  The  workers along the belt are  the divider,
the multiplier, the adder and the sine-table-looker-upper, standing in the order
that we would normally solve the equation.  Here comes the first I.  The divider
picks up his number cruncher and  divides it by 512.  Meanwhile the rest  of the
guys multiply, add and sine-table-lookup  whatever junk the janitor left  on the
belt last  night, maybe his  lunch or something.   They all place  their results
down on the belt, and it is carried along to the next one.  The divider's number
is now in front of the guy who multiplies by 2π.  Meanwhile, THE NEXT VALUE OF I
comes down the chute  towards the divider!  Notice  we don't have a  valid value
for Y at the other end yet.   But if we continue, we will.  So, the  divider and
the 2π multiplier get to work doing their thing, meanwhile, the rest of the guys
continue playing with  the janitor's lunch  as it moves  past them on  the belt,
rolling it into little balls and throwing it at each other.  They set down their
results, and now the adder gets the intermediate value of what was the  first I,
the 2π multiplier gets the next, and the divider gets yet another new I.  On the
next shift, the  sine-table-looker-upper does his thing,  and the value  for the
first I is  through the process,  transformed into Y.   Behind it are  the other
intermediate values.  So we need to crank the whole process three more  times to
get the  5th Y  value through  the line.   But notice,  during these  next three
passes, we get a valid Y  value every time the assembly line turns  around once.
That means  we get  a good  result in the  time of  only one  intermediate step,
whereas in the block process, we  would have got only one result for  every five
intermediate steps.  This  is a nice increase  in average computing  time.  It's
called pipelining.

Lets look at some  of the implications of this  process.  First of all,  all the
inner operations are similar in that they constitute the picking up of a number,
the operation  on it,  and replacing  it.  We'll  call this  basic task  a pass.
Furthermore, it should  be obvious that the  system cannot work any  faster than
the slowest processor.  The minimum execution time of a pass then is the time it
takes  for  the  slowest  computing  element.   So  the  accomplishment  of  one
intermediate result consists of a pass.

Notice  that the  processor must  be primed  like a  pump., but  once it  is, it
produces one good value of output every pass.  In general, if the pipeline  is N
elements long, we  must add N passes  to the number of  passes it would  take to
send all the numbers through the pipeline.

Notice also we must throw away the first N samples we get of the output.  In the
assembly line above, the processor was  four units long, and we had  five values
of I to calculate, so it took  nine passes to accomplish the task, and  we threw
away the first four values of  the output since they were undefined  (unless you
knew the janitor).


                                    Page 73
LRNSAM                                                                     DRAFT


                                   Appendix B
                                   ________ _

                           Time division multiplexing
                           ____ ________ ____________



The Samson box is extensively pipelined.  Just how extensively we will soon see.
Two of the essential ingredients in the box are called processing elements.  One
of them is  called a modifier,  the other is  called a generator.   The modifier
does various flavors of arithmetic and logic testing, while the generator does a
fancier version  of our example  formula, but with  lots of bells  and whistles.
Pete Samson claims there are 256 generators and 128 modifiers in his box,  but I
happen to  know on high  authority that it  ain't necessarily so.   Our reliable
sources who  say they  have positive  proof have  insinuated that  what's really
there  is only  256  VIRTUAL generators  and  128 virtual  modifiers.   In fact,
there's  only  one  nest of  electronics  for  the generators  and  one  for the
modifiers.  The rest  of it is done  with mirrors...  electronic  mirrors.  What
really goes on is  that there are 256 sets  of parameters for the  generator and
128 sets of  parameters for the modifier,  and the processing elements  zap from
one set to  the next at a  truely blinding rate of  speed.  This is  called time
division multiplexing.

To get the full picture, reload  your mental image of the example  of pipelining
above.  Think of it maybe this way:

 I[1]   I[2]    I[3]    I[4]    I[5]    *       *       *       *
__↓__
| / |
| * |
| + |
|sin|
--↓--
  *     *       *       *       Y[1]    Y[2]    Y[3]    Y[4]    Y[5] 


                                    Fig. 20
                                    ____ __


Each step the  box takes from  left to right accomplishes  one pass, and  on the
fifth pass, we start geting valid Y values.  Now imagine the process if  we want
to deal with many  streams of data.  All we  need do is interleave  the seperate
streams.  Input I[1] will come out as  output X[1], J will come out as Y,  and K
as Z.




                                    Page 74
LRNSAM                     Time division multiplexing                      DRAFT



 I[1] J[1] K[1] I[2] J[2] K[2] I[3] J[3] K[3] I[4] J[4] K[4]
___
| / |
| * |
| + |
|sin|
-- -
  -
  *     *    *    *  X[1] Y[1] Z[1] X[2] Y[2] Z[2] X[3] Y[3] Z[3]...


                                    Fig. 21
                                    ____ __

Before analyzing this, let's redefine  our terms.  What we were calling  a pass,
                                                                           ____
lets now call a tick.  So a tick is the act of moving the processor,  sucking up
                ____
a number and spitting out another.  Now let's redefine "pass" to mean that point
where we have produced one valid  output for all of the streams.   (Don't worry,
we'll stick to this definition. The  term pass can be used in  simple situations
like our first example,  but from here on we  reserve the word pass to  refer to
the period culumnating in  the output of samples.) So,  where is the end  of the
first pass?  Right!, at Z[1], after  seven ticks.  Where is the end of  the next
pass?  Right again, at Z[2], after  three ticks.  From here on, a  pass consists
of three ticks only.   Since one pass equals one  valid output, if we  have five
each of I, J and K, it will take five passes of three ticks per pass to get them
all through  PLUS four  ticks to  prime the processor  for a  grand total  of 19
ticks.

We can deduce from this that the number of ticks per pass is equal to the number
of parallel data streams, also that the total number of ticks is equal to:


   <# of streams>*<length of longest stream> + <length of pipeline>.     Equ. 16

Remember that  a pass consists  of all the  processing necessary to  produce one
complete set of  output data, that  is, one sample.   The tick is,  therefore, a
subspecie of the pass.   The tick is the smallest  unit of time our  process can
use and the time it takes is the time of the slowest process.











                                    Page 75
LRNSAM                                                                     DRAFT


                                   Appendix C
                                   ________ _

                              Basic number theory
                              _____ ______ ______



Trying to make representations between different number systems is  somewhat the
same problem as finding suitable translations for unidiomatic foreign  terms, we
have to agree on a meaning.

Modern computers represent  numbers in a binary  system.  Briefly what  a binary
number consists of in  such a machine is a  group of electronic devices  each of
which can  be "on" or  "off", depending on  the number being  represented.  Each
device stands for a particular  weight or magnitude, and the  number represented
by the set of devices is the  weighted sum of the ones that are "on".   Thus, if
we let  three printing  columns represent three  such devices  and we  wanted to
represent the number 3 we would do this;:

weight:          4       2       1
state:           0       1       1

                                    Fig. 22
                                    ____ __


where a 1 means "on" and 0 means "off".  The word binary refers to the fact that
the  devices have  only these  two states.   The general  expression for  such a
number is


   m      2    1    0       -1     -2        -n
b 2 +...b  +b 2 +b 2  ∧ b  2  +b  2  +...b  2
 m       2   1    0      -1     -2        -n


where the "∧" is the "binary point" (think of decimal point).  The b term is the
state, m is the largest weight, n the smallest.  The largest unsigned integer we
                          m
could represent would be 2 -1 where m is the number of places, or "bits" left of
the binary  point.  In  Fig. 22,  7 is  the largest  number we  could represent,
starting from zero.

For representing  the domain of  positive and negative  numbers there  are three
basic systems used: sign-magnitude,  one's complement and two's  compliment.  In
the  sign-magnitude system  the leftmost  bit, called  the most  significant bit
(MSB), or high order bit, is used  to denote the sign of the number,  zero being


                                    Page 76
LRNSAM                        Basic number theory                          DRAFT


taken as positive, one as  negative.  So the highest integer magnitude  that can
                   m-1
be represented is 2   -1.

One's complement and two's complement do things a little differently.   Here the
negative  numbers are  expressed  as the  logical complement  of  their positive
counterpart.  Complementation is done by changing all the zeros to ones and vice
                                                                             n
versa.  For  one's complement the  formula for the  negative equivalent  is 2 -x
where n is the number  of bits and x is  the number to be complemented.   So the
one's complement  of 001  is 110,  which we represent  in decimal  as +1  and -1
                                                       n            n
respectively.  For two's  complement the formula  is (2 -1)-x or  (2 -x)-1.  For
example, to form the two's complement of 010, subtract one = 001, and complement
=  110.   Or, equivalently,  complement  010 =  101  and add  one  to  the least
                                                         ___
significant bit (or LSB) = 110.  To show this graphicly:
binary two's   one's   sign-magnitude
011    +3      +3      +3
010    +2      +2      +2
001    +1      +1      +1
000    +0      +0      +0
111    -1      -0      -3
110    -2      -1      -2
101    -3      -2      -1
100    -4      -3      -0

Notice that the one's complement and sign-magnitude have two representations for
zero, whereas the  two's complement has  one zero and  it is taken  as positive.
Two's complement also has one negative number greater than its  largest positive
number.   This  number has  no  complement.   Notice that  the  two's  and one's
complement  system  "wrap  around"  from the  greatest  positive  number  to the
greatest  negative  number  when the  greatest  positive  number  is incremented
whereas the sign-magnitude system goes to zero again.

Now the Samson box uses two systems, the two's complement for signed numbers and
plain binary for unsigned  numbers.  Two's complement offers some  advantages to
the computer designer and is nearly universal (exept for certain CDC machines!).

So far so good, now what if we wanted to represent fractional numbers instead of
integers?  Well, for example, using  the unsigned system, and expanding  to four
bits it might look something like this:








                                    Page 77
LRNSAM                        Basic number theory                          DRAFT


binary   fractional    decimal
1111     nearly 1.0    15
1110                   14
1101                   13
1100     .75           12
1011     nearly .75    11
1010                   10
1001                   9
1000     .5            8
0111     nearly .5     7
0110                   6
0101                   5
0100     .25           4
0011     nearly .25    3
0010                   2
0001                   1
0000     .0            0

                                                  n
The fractional value is then the <decimal value>/2 .  The numbers just below the
the ones that have only one bit  on (.5, .25, etc.) are often taken as  being an
equivilent for  the ones with  only one bit,  which is why  I say they  are, for
example, nearly .5, etc.

The two's complement version of this is:
binry    fractioal     decimal
0111     nearly 1.0    7
0110                   6
0101                   5
0100     .5            4
0011     nearly .5     3
0010     .25           2
0001     .125          1
0000     .0            0
1111     nearly .0     -1
1110     -.125         -2
1101     -.25          -3
1100     -.5           -4
1011                   -5
1010                   -6
1001     nearly -1.0   -7
1000     -1.0          -8

Here we have a  -1.0, but only a +.999...  or nearly +1.0.  So it  conforms with
the two's complement system which has one greater negative value than positive.

This has the formal name of two's complement fixed binary point notation.  It is


                                    Page 78
LRNSAM                        Basic number theory                          DRAFT


used everywhere  in the box  that a  signed system is  needed, most  notably for
waveforms, which are representations of positive and negative fluxuations around
normal air pressure taken as zero.   However, it is not used in  simple counting
situations such  as ANGLE  or EXPONENT  where the  unsigned integer  notation is
used.

Notice the sequence in the fractional notations that .5 = 0100..., .25 = 0010...
and .0125 = 0001... (where "..."  stands for any additional bits to  the right).
From this it can be seen that to multiply a number by a (positive) power of two,
N, requires only that it be shifted  left N places.  For division by a  power of
                                                                              2
two, shift  right.  This also  holds for integer  binary systems: ...0010  = 2 ,
           4
...0100 = 2 , etc.

Be careful as to whether a  number is signed or unsigned when combining  them in
expressions as  signed numbers are  right shifted one  bit compared  to unsigned
numbers (which use the high order bit for magnitude, not sign).

This also brings  up the general problem  of adding words of  different lengths.
Clearly, to get a  valid result for an  addition or subtraction, the  weights of
the terms must match.  If the  words are of different lengths, the  procedure is
to first normalize the two numbers  (line up the weights), then extend  the sign
bit of the shorter word out to the limit of the most significant bit, then add.

























                                    Page 79
LRNSAM                                                                     DRAFT


                                   Appendix D
                                   ________ _

                                 SAIL examples
                                 ____ ________





D-1. Generator example

In this  and the following  examples, there  has been an  attempt to  solve some
programming problems using the lowest level routines in a one-at-a-time  way for
didactic purposes.  In practice, one would probabily not approach  writing music
this way.  For this, various higher level routines are being written  which will
optimize both the programming  and the programmer's time.  Read  these examples,
therefore, as an excersize in comprehension, not programming.

This is  a complete call  to the  box, corresponding to  the instrument  SIMP in
MUSCMP, with pitch A440 and duration of 6 seconds.

begin "SIMP"

define ⊂ = "COMMENT";
⊂ get the source file definitions;
Require "LOWER.DEF[SAM,MUS]" source_file; 
Require "LOWER.REL[SAM,MUS]" load_module; 

integer  
com_chan, ⊂ will be the command channel from the program to the box 
                and to a file;
simp, ⊂ will contain a pointer to the oscillating generator;
srate, ⊂ will be the sampling rate;
brk, eof, fail, ⊂ flags pertaining to SAIL i/o;
nptix, ⊂ number of processing ticks;
frmag, ⊂ magic number for FREQUENCY;
dfrmag, ⊂ magic number for SWEEP (won't be used in this example);
nutix, ⊂ number of update ticks;
gpmag, ⊂ magic number for RATE;
outa, ⊂ will contain a sum memory address connected to a generator
        in WRITE_DAC mode;
gen_outa, ⊂ will be the genrator in WRITE_DAC mode;
zero; ⊂ a dummy sum memory location which will always be zero;
string s;

procedure SET_CLOCK(integer nptix,nutix); 
        ⊂ sets clock rate and updates magic numbers;
    begin
        set_field(processing_ticks,nptix);

                                    Page 80
LRNSAM                           SAIL examples                             DRAFT


        set_field(total_ticks,nptix+nutix);
        srate←1/(0.000000195*(nptix+nutix+9));
        frmag←(1 lsh 28)/srate; ⊂ frmag is the scaling 
                for FREQUENCY;
        dfrmag←frmag/srate; ⊂ dfrmag is the scaling for SWEEP;
        gpmag←(1 lsh 24)/srate; ⊂ gpmag is the scaling for RATE;
    end;

integer procedure GET_OUTN(integer chan; reference integer gen);
        ⊂ Gets DAC output generators;
begin
        integer sm;
        gen←get(id_generator);
        sm←get(id_sum_memory);
        bind(gen,fm,sm);
        bind(gen,sweep,chan);   
                ⊂ DAC number is put in the sweep slot;
        bind(gen,mode,dac_write);       
                ⊂ Start up generator feeding DAC;
        return(sm); ⊂ adr. will be used by generators 
                desiring output to DAC;
end;

        ⊂ We want a record of the command stream sent to the box.
        To do this, get a channel, use it to open a disk file, then
        also pass it to SET_OUTPUT;
com_chan←getchan;
open(com_chan,"dsk",'17,0,0,0,eof,fail);
do begin
        outstr("output file = ");
        enter(com_chan,s←inchwl,fail);
end until not fail;
set_output(com_chan); ⊂ this passes the channel number 
        to the box assembler;

simp←GET(id_generator); ⊂ gloms a generator and returns its address
        and other bits in simp;

SET_CLOCK(32,32); ⊂ produces srate of 70249 samples/sec.;

BIND(simp,sweep,0); ⊂ set up oscilator params., this one for 
        sweep rate = 0;
BIND(simp,angle,0); ⊂ initial angle = 0, so we get a sine wave.
        Had it been (2↑20)/4, it would have been a cosine wave, 
        since it would start at 90 degrees;
zero←GET(id_sum_memory);
BIND(simp,fm,zero); ⊂ the FM term is always read, 
        so if not wanted point it into sum memory where it will only

                                    Page 81
LRNSAM                           SAIL examples                             DRAFT


        get zeros;
BIND(simp,scale,0);
BIND(simp,exponent,0); ⊂ set up initial point in ramp;
BIND(simp,asymptote,0); ⊂ set up envelope d.c. offset (that is, we
        don't want any);
BIND(simp,rate,gpmag*2); 
        ⊂ set up envelope attack time to be 2 sec. and direction 
        to be forward;
⊂ all this produces a linear positive-going attack 2 sec;
BIND(simp,frequency,frmag*440); ⊂ a sine wave with period 440 Hz;

outa←GET_OUTN(1,gen_outa); ⊂ a generator now desiring to pass 
        samples to a dac need only put the samples in sum memory in
        location OUTA;
BIND(simp,sum_memory,outa);

⊂ crank it up;
BIND(simp,mode,a_running+lplusq+cosine); ⊂ set up simp run &
        envelope & oscil. modes;
        ⊂ beat me, daddy, 8 to the bar...;
SET_FIELD(dwell, 2*srate); ⊂ let it run for 2 sec.;

BIND(simp,rate,0); ⊂ set steady state.  Since rate is now 0, it will
not change the value in EXPONENT;
SET_FIELD(dwell, 2*srate); ⊂ let it run for 2 sec.;

BIND(simp, rate, gpmag*-2); ⊂ This moves the envelope retrograde;
SET_FIELD(dwell, 2*srate); ⊂ for 2 sec.;

GIVE(simp); ⊂ close it up;
GIVE(gen_outa);
GIVE(outa);
⊂ GIVing back these elements is not necessary here, but in principle,
whenever you aren't using a p.e., GIVE it back so subsequent GETs 
don't run down your sampling rate;
FLUSH; ⊂ don't forget to wash your hands;
close(com_chan);

end "SIMP";



D-2. Modifier example

Everybody's favorite, a random pitch, random duration instrument.

This instrument is designed  with the purpose of letting  the box do as  much of


                                    Page 82
LRNSAM                           SAIL examples                             DRAFT


the  processing as  possible.  It  seems confoundingly  complex in  view  of the
simple sonic result.  It would no  doubt have been simpler to have had  a random
number generator in  the SAIL command program  create a score of  random pitches
and durations,  which would  be sent to  a relatively  simple instrument  in the
box...  The two  principle arguments against this  later approach stem  from the
fact that the less you rely on the interactive system the more on  schedule will
be your results.  First, you save I/O operations between the  interactive system
and the box (which can  kill real-time interaction), second, the box  will never
swap you out.

This uses four modifiers, two modifiers in tr_u_noise mode to produce the random
numbers, and two  others in mixing  mode to scale  the random numbers  to values
usable for frequencies and durations.  The two modifiers in tr_u_noise  mode are
triggered by a generator in pulse_train mode.  The number from one of  the noise
modifiers is  scaled by another  modifier to a  useful range for  durations. The
scaled number in  turn sets the period  of the triggering generator.   The pulse
from the  triggering generator  also triggers the  other modifier  in tr_u_noise
mode. Its  number, after  similar scaling by  another modifier,  is sent  to the
frequency port of a second generator in sinewave mode.  The output of the second
generator is then fed to the DAC.

The scaling is performed as follows: We need a number to determine the period of
the triggering generator.  Its  port from sum memory,  FM, reads a 20  bit word.
We want it  to have values between,  say a and b,  giving us a range  of periods
between say  t0 and t1.   That is, we  want the range  to go from  a←t0*srate to
b←t1*srate.  So  the number we  want to feed  to the FM  port of  the triggering
generator is the increment  that will cause the  generator to turn over  all its
angle bits (all 20  of them) in that length  of time.  The increment we  feed to
the generator then would be the reciprocal of the total time we want it to spend
per  period.  That  is,  we ultimately  want  a number  between  1/(a*srate) and
1/(b*srate).  Now the number we get from a modifier in tr_u_noise mode (actually
from any  modifier mode  save int_mixing)  is a  number between  1 and  -1.  The
general formula for scaling is

        x  - L0
  Y =   ------- (a - b) + b
        L1 - L0
where L1 is the highest value and L0 is the lowest (in this case 1 and -1), x is
the actual value produced by the  modifier, and where Y is the  resulting value,
which in this case is used for the period.  This becomes, then

        x+1
    =   ---(a-b) + b.
         2
But we don't  have a modifier  that can do all  of this at  once, so we  need to
break it down further:



                                    Page 83
LRNSAM                           SAIL examples                             DRAFT


        x+1
    =   ---(a-b) + b
         2

         a-b   a-b
    =   x--- + --- + b
          2     2

         a-b   a+b
    =   x--- + ---
          2     2

We can  precalculate (a-b)/2 and  give it  to the coefficient  of a  modifier in
mixing mode, where it would be multiplied by x.  That leaves us with  the second
term (a+b)/2.  This  term can be  inserted in the  FREQUENCY port of  the scaled
generator, where it  will be added to  the term being read  by its FM  term. The
next pages contain a complete call to the Samson box to realize this example.
































                                    Page 84
LRNSAM                           SAIL examples                             DRAFT


begin "RAND"

define ⊂ = "COMMENT";

⊂ this is a random duration and pitch instrument;

Require "LOWER.DEF[SAM,MUS]" source_file; ⊂ get the source file definitions;
Require "LOWER.REL[BOX,MWK]" load_module; ⊂ get the source file definitions;

integer  
com_chan,       ⊂ will be the command channel from the program to 
                the box and to a file;
sum_scale,      ⊂ sum memory loc. where gen_clock gets its period;
gen_clock,      ⊂ will contain a pointer to the pulsing generator;
sum_trigger,    ⊂ sum memory where gen_clock will write its pulse;
mod_clock,      ⊂ will be a modifier in TR_U_NOISE mode, which will read
                sum_trigger and put out random numbers between 1 and -1
                into sum_x where it will be picked up by mod_scale
                for duration scaling;
sum_x,          ⊂ sum memory where mod_clock writes its output;
mod_scale,      ⊂ modifier in mixing mode which will scale value in sum_x
                and pass it to sum_scale, where gen_clock reads it;
mod_frq,        ⊂ will be a modifier also in TR_U_NOISE mode, which will also
                get its trigger from sum_trigger, but will pass its 
                random value on to sum_frq;
sum_frq,        ⊂ where mod_frq writes and where mod_tessitura reads;
mod_tessitura,  ⊂ scales value from sum_frq for frequency range;
sum_tessitura,  ⊂ where mod_tessitura writes and gen_frq reads;
gen_frq,        ⊂ will contain a pointer to the oscillating generator;
sum_outa,       ⊂ where gen_frq will write its frequency output;
gen_outa,       ⊂ will be the genrator in WRITE_DAC mode;
sum_zero,       ⊂ a sum memory location never written into, and therefore
                when read from will always return zero.  This is the way
                to get zeros into places like TERM_0 and FM, etc.;
t0,             ⊂ upper limit of period scaled for passes;
t1,             ⊂ lower limit of period scaled for passes;
f0,             ⊂ upper frequency limit scaled;
f1,             ⊂ lower frequency limit scaled;
time_constant1, ⊂ will contain first scaling term,(a-b)/2;
time_constant2, ⊂ will contain second scaling term,(a+b)/2;
frq_constant1,  ⊂ will contain first scaling term for frequency (a-b)/2;
frq_constant2,  ⊂ will contain second scaling term for frequency (a-b)/2;
srate,          ⊂ will be the sampling rate;
brk, eof, fail, ⊂ flags pertaining to SAIL i/o;
frmag,          ⊂ magic number for FREQUENCY;
gpmag,          ⊂ magic number for RATE;
dfrmag,         ⊂ magic number for SWEEP (won't be used in this example);
comag,          ⊂ magic number for COEFF0 and COEFF1;

                                    Page 85
LRNSAM                           SAIL examples                             DRAFT


nptix,          ⊂ number of processing ticks;
nutix,          ⊂ number of update ticks;
max30,
max28,
max20,
max12,
attack,         ⊂ attack time;
dur,            ⊂ steady state;
decay;          ⊂ decay time;
string s;       ⊂ string storage;


procedure SET_CLOCK(integer nptix,nutix); 
        ⊂ sets clock rate and updates magic numbers;
    begin
        set_field(processing_ticks,nptix);
        set_field(total_ticks,nptix+nutix);
        srate←1/(0.000000195*(nptix+nutix+9));
        frmag←(1 lsh 28)/srate; ⊂ frmag is the scaling for FREQUENCY;
        dfrmag←frmag/srate; ⊂ dfrmag is the scaling for SWEEP ≡ 2↑28/srate↑2;
        gpmag←(1 lsh 24)/srate; ⊂ gpmag is the scaling for RATE;
        comag←(1 lsh 30)/srate; ⊂ scaling for COEFF0 and COEFF1;
    end;

integer procedure GET_OUTN(integer chan; reference integer gen);
        ⊂ Gets DAC output generators;
    begin
        integer sm;
        gen←get(id_generator);
        sm←get(id_sum_memory);
        bind(gen,fm,sm);
        bind(gen,sweep,chan);   ⊂ DAC number is put in the sweep slot;
        bind(gen,mode,dac_write);       ⊂ Start up generator feeding DAC;
        return(sm); ⊂ adr. will be used by generators desiring output to DAC;
    end;

simple integer procedure DATE;
    begin "DATE"
        ⊂ gets today's date in system format, we'll use this to prime
                TERM_1 of the modifiers in tr_u_noise mode;
        define date = '047540000014;
        integer i;
        quick_code
        date;
        movem '13,i;
        end;
        return(i);
    end "DATE";

                                    Page 86
LRNSAM                           SAIL examples                             DRAFT


        
        ⊂ this will be used to set up upper and lower bounds for
        random selection;
simple procedure SET_RANGE(string s; reference integer upper, lower);
    begin "RANGE"
        string str;
        integer brk;
        print("Upper ",s," range = ");
        upper←1/intscan(str←inchwl,brk);
        print("Lower ",s," range = ");
        lower←1/intscan(str←inchwl,brk);
    end "RANGE";

        ⊂ to read in parameters;
simple integer procedure IIN(string s); 
    begin "IIN"
        integer brk;
        string st;
        print(s," = ");
        return(intscan(st←inchwl,brk));
    end "IIN";
        ⊂ We want a record of the command stream sent to the box.  To do this,
        get a channel, use it to open a disk file, then also pass it to
        SET_OUTPUT;
        com_chan←getchan;
open(com_chan,"dsk",'17,0,0,0,eof,fail);
do begin
        outstr("output file = ");
        enter(com_chan,s←inchwl,fail);
end until not fail;
set_output(com_chan); ⊂ this passes the channel number to the box assembler;

MAX30←'7777777777;
MAX28←'777777777;
MAX20←'77777777;
MAX12←'7777;

        ⊂ GET the processing elements and return their addresses;
sum_scale←GET(id_sum_memory);
gen_clock←GET(id_generator); 
sum_trigger←GET(id_sum_memory);
mod_clock←GET(id_modifier);
sum_x←GET(id_sum_memory);
mod_scale←GET(id_modifier);
mod_frq←GET(id_modifier);
sum_frq←GET(id_modifier);
mod_tessitura←GET(id_modifier);
sum_tessitura←GET(id_sum_memory);

                                    Page 87
LRNSAM                           SAIL examples                             DRAFT


gen_frq←GET(id_generator);
sum_zero←GET(id_sum_memory);
        ⊂ sum_outa and gen_outa will be set up later;

SET_CLOCK(32,32); ⊂ produces srate of 70249 samples/sec.;
SET_RANGE("duration",t0,t1);
time_constant1←frmag*(t0-t1)/2;
time_constant2←((t0+t1)/2)*comag;
SET_RANGE("frequency",f0,f1);
frq_constant1←frmag*(f0-f1)/2;
frq_constant2←((f0+f1)/2)*comag;
attack←IIN("Attack time");
dur←IIN("Duration");
decay←IIN("Decay");

        ⊂ Begin patching the processing elements together;
        ⊂ Set up gen_clock;
BIND(gen_clock,sweep,0); 
BIND(gen_clock,angle,0); 
BIND(gen_clock,fm,sum_scale); ⊂ gen_clock reads sum_clock;
BIND(gen_clock,scale,0);
BIND(gen_clock,exponent,0); 
BIND(gen_clock,asymptote,max12); 
        ⊂ we want the envelope side to not scale the result
        of the oscillator side, so we will get a good fat trigger;
BIND(gen_clock,rate,0); 
BIND(gen_clock,frequency,time_constant2); 
        ⊂ this sets up the second term of the scaling
        described in the introduction to this example;
BIND(gen_clock,sum_memory,sum_trigger);

        ⊂ set up mod_clock;
BIND(mod_clock,b_in,sum_trigger);
BIND(mod_clock,coeff1,max30); 
        ⊂ could be any value except 0, which would clobber the
        trigger detector;
BIND(mod_clock,b_in,sum_trigger); 
BIND(mod_clock,term_0,0); ⊂ leave it at zero, sez Andy;
BIND(mod_clock,term_1,date); ⊂ date is a guaranteed unique number;
BIND(mod_clock,coeff0,'46445); ⊂ Knuth's magic number;
BIND(mod_clock,coeff1,1); 
BIND(mod_clock,b_scale,1); ⊂ we must LSH the result of the modifier one bit
                           since the mod. output is signed, but the input to
                           the generator is not.  This pushes the sign bit
                           off the left of the word;
BIND(mod_clock,sum_memory,sum_x); 

        ⊂ set up mod_scale;

                                    Page 88
LRNSAM                           SAIL examples                             DRAFT


BIND(mod_scale,a_in,sum_x);
BIND(mod_scale,b_in,sum_zero);
BIND(mod_scale,coeff0,time_constant1); 
        ⊂ this is the first term for period scaling
        discussed on the introduction to this example;
BIND(mod_scale,coeff1,0);
BIND(mod_scale,sum_memory,sum_scale);
        ⊂ this concludes patching of the period control;

        ⊂ set up mod_frq;
BIND(mod_frq,b_in,sum_trigger);
BIND(mod_frq,coeff0,'57445); ⊂ a different number;
BIND(mod_frq,coeff1,1);
BIND(mod_frq,term_0,0); 
BIND(mod_frq,b_scale,1);
BIND(mod_frq,sum_memory,sum_frq); 

        ⊂ set up mod_tessitura;
BIND(mod_tessitura,a_in,sum_frq);
BIND(mod_tessitura,b_in,sum_zero);
BIND(mod_tessitura,coeff0,frq_constant1); 
        ⊂ this is the first term for frequency scaling
        discussed on the introduction to this example;
BIND(mod_tessitura,coeff1,0);
BIND(mod_tessitura,sum_memory,sum_scale);

        ⊂ set up gen_frq;
BIND(gen_frq,sweep,0); 
BIND(gen_frq,angle,0); 
BIND(gen_frq,fm,sum_tessitura); ⊂ gen_frq reads sum_tessitura;
BIND(gen_frq,scale,0);
BIND(gen_frq,rate,attack*gpmag); ⊂ set up to attack;
BIND(gen_frq,exponent,0); 
BIND(gen_frq,asymptote,0); 
BIND(gen_frq,frequency,frq_constant2); 
        ⊂ this sets up the second term of the frequency
           scaling described in the introduction to this 
           example;
sum_outa←GET_OUTN(1,gen_outa);
BIND(gen_frq,sum_memory,sum_outa);

        ⊂ crank it up;
BIND(gen_clock,mode,a_running+lplusq+pulse_train); ⊂ set up gen_clock run & 
        envelope & oscil. modes;
BIND(mod_clock,mode,tr_u_noise);
BIND(mod_frq,mode,tr_u_noise);
BIND(mod_scale,mode,mixing);
BIND(mod_tessitura,mode,mixing);

                                    Page 89
LRNSAM                           SAIL examples                             DRAFT


BIND(gen_frq,mode,a_running+lplusq+cosine);

SET_FIELD(dwell, attack*srate); 
BIND(gen_frq,rate,0);
SET_FIELD(dwell, dur*srate); 
BIND(gen_frq,rate,-decay*gpmag);
SET_FIELD(dwell, -decay*srate); 
        ⊂ o.k., enough of this nonsense, now we turn them all off in
        inverse order;
BIND(gen_frq,mode,g_inactive);
BIND(mod_tessitura,mode,m_inactive);
BIND(mod_scale,mode,m_inactive);
BIND(mod_clock,mode,m_inactive);
BIND(mod_frq,mode,m_inactive);
BIND(gen_clock,mode,g_inactive);
BIND(gen_outa,mode,g_inactive);

GIVE(gen_clock); ⊂ close it up;
GIVE(gen_frq); 
GIVE(gen_outa);
GIVE(mod_clock);
GIVE(mod_scale);
GIVE(mod_tessitura);
GIVE(mod_frq);
GIVE(sum_scale);
GIVE(sum_trigger);
GIVE(sum_x);
GIVE(sum_frq);
GIVE(sum_tessitura);
GIVE(sum_outa);
FLUSH; ⊂ don't forget to wash your hands;
close(com_chan);
release(com_chan);

end "RAND";














                                    Page 90
LRNSAM                                                                     DRAFT


                                   Appendix E
                                   ________ _

                               Delay line example
                               _____ ____ _______



The following is  supplied as a  terse example of using  a delay line  for table
lookups.  It just  shows  how to  connect  delay memory  to  a delay  line  to a
modifier, and how to  fill the delay memory.  Left  out are all the  settings of
the modifier perameters and read-write addresses to sum memory and the interface
to either the DACs or a file.
begin "delay"

require "lower.def" source_file;
require "lower.rel" load_module;
integer delay_port,mod;
define ⊂ = "comment ";
define len = "10";
define loc = "1033"; ⊂ some random place in delay memory;
preload_with 0,1,2,3,4,5,6,7,8,9;
safe integer array table[0:len-1];

load_delay(table,loc,len); ⊂ stuff array TABLE into someplace in delay memory;
mod ← get(id_modifier);
delay_port ← get(id_delay);
bind(delay_port,mode,table_lookup); ⊂ set to table lookup mode;
bind(delay_port,base_address,loc); ⊂ this delay starts at loc;
bind(delay_port,delay_length,len); ⊂ len lookups;
bind(delay_port,index,len/2); ⊂ set index to some location or other;
bind(mod,invoke_delay_unit,delay_port); ⊂ delay_port is bound to mod;

⊂ we assume great things happen here;

⊂ now lets change the function of the delay unit to delay line mode;
bind(delay_port,mode,delayline); ⊂ We can now use this as a delay line;

⊂ More great things happen here;

give(delay_port); ⊂ OK, give it back!;
give(mod);

end "delay";







                                    Page 91
LRNSAM                         Delay line example                          DRAFT


E-1. Writing your own procedures

The following is an example of how you could write your own procedures to handle
box setup.  This routine gets N consecutively numbered generators.  It  looks up
array UNIT_GENERATORS which contains a location for all the elements in  the box
and whether they are in use  or not.  It requires a global variable  NPTIX which
would contain the number of  processing ticks available on this pass,  and hence
the number of processing elements that can run.  It also needs to know what kind
of  processing  element  you   want,  specifying  it  by   saying  ID_GENERATOR,
ID_MODIFIER , ID_DELAY or ID_SUM_MEMORY which are defined in LOWER.DEF to  be 0,
1, 2 and 3  respectively.  The routine returns the  number of the first  unit in
the sequence. If there are not N units in a row, it returns -1.

integer procedure GET_N_UNITS(integer TYPE, NLOCS);
    begin "GET_N"
        integer I,J,BASE,LIMIT,NEED_N_TIX;
        define ⊂ = "COMMENT";
        boolean GOTIT;

        ⊂ BASE contains the base address of the type
                of element in UNIT_GENERATORS;
        base← case TYPE of(0,256,384,640);
        ⊂ now figure out how many processing ticks the desired
                number of processing elements would take;
        case type of
        begin
                [0] need_n_tix←nptix; ⊂ generators take only one
                        processing tick;
                [1] need_n_tix←nptix/2; ⊂ it takes two processing
                        ticks per modifier;
                [2] need_n_tix←nptix; ⊂ delay memory takes no
                        processing ticks;
                [3] need_n_tix←(nptix-6)/4  ⊂ this is the formula
                        for available delay cycles;
        end;

        ⊂ LIMIT has maximum processing ticks for all of a
                type of element to be running;
        limit← case type of(256,256,0,134);
        if nlocs > need_n_tix then
        begin
                outstr("You are too greedy");
                return(0);
        end;

        gotit←false;
        for i←0 step 1 until NPTIX-NLOCS do
        begin "ISTART"

                                    Page 92
LRNSAM                         Delay line example                          DRAFT


                if ¬UNIT_GENERATORS[i+base] then
                begin "CHKIT"
                    for j←i+1 step 1 until i+NLOCS-1 do
                        if UNIT_GENERATORS[j+base] then
                        begin "NOGOOD"
                            i←j;
                            continue "ISTART";
                        end "NOGOOD";
                    gotit←true;
                    done "ISTART";
                end "CHKIT";
        end "ISTART";
        if ¬gotit then return(-1);
        for j←i step 1 until i+NLOCS-1 do
            UNIT_GENERATORS[j+base]←true;
        return(i);
    end "GET_N";
































                                    Page 93
LRNSAM                                                                     DRAFT


                                   Appendix F
                                   ________ _

                        Generator field and mode tables
                        _________ _____ ___ ____ ______





F-1. Generator parameters
ID    SIZE  DEFINITION FUNCTION
--------------------------------------------------------------------------------
GO    20    SWEEP      oscillator frequency sweep rate
GJ    28    FREQUENCY  oscillator frequency
GK    20    ANGLE      oscillator angle
GN    11    NCOSINES   number of cosines to be summed
GM    4     SCALE      binary scale of cosine or sum of cosines
GP    20    RATE       envelope rate of change
GQ    24    EXPONENT   envelope current value
GL    12    ASYMPTOTE  asymptote (DC offset of the envelope)
GSUM  6     SUM_MEMORY sum memory location into which
                       output is added
GFM   7     FM         sum memory address from which frequency
                       modulation data is taken
GMODE 10    MODE       generator mode, including run mode and
                       type of oscillator and enveolpe processing
                       (see list of modes on page 22).
            OSC_MODE   sets oscillator field of GMODE without
                       altering run mode or envelope mode.
            ENVELOPE   sets envelope field of GMODE without
                       altering run mode or oscillator mode.



F-2. Generator run modes
Definition             Osc. run? Envelope run?   Add to sum?
--------------------------------------------------------------------------------
G_INACTIVE             no        no              no
G_PAUSE                no        no              no
G_WAIT                 yes       no              no
x_RUNNING:
   A_RUNNING           yes       yes             yes
   B_RUNNING           yes       yes             yes
   C_RUNNING           yes       yes             yes
DATA_READ              no        yes             yes
DATA_WRITE             no        no              no
DAC_WRITE              no        no              no



                                    Page 94
LRNSAM                  Generator field and mode tables                    DRAFT


F-3. Oscillator modes

--------------------------------------------------------------------------------
   SAWTOOTH        sawtooth wave
   SQUARE          square wave
   SUM_OF_COSINES  sum of cosines
   PULSE_TRAIN     pulse train
   COSINE          sine(ANGLE),normal sine wave
                   mode (with fm).
   COS_FM          cosine(FREQUENCY + FM)
                   special sine table lookup mode.



F-4. Envelope modes
Definition Function
--------------------------------------------------------------------------------
LPLUSQ     Add EXPONENT (GQ) to the offset constant
           in ASYMPTOTE (GL).
LMINUSQ    Subtract EXPONENT (GQ) from offset constant
           in ASYMPTOTE(GL)
                                  (-EXPONENT (GQ))
LEXPLUS    Add ASYMPTOTE (GL) to 2                 and scale.
                                       (-EXPONENT (GQ))
LEXMINUS   Subtract ASYMPTOTE (GL) to 2                 and scale.
























                                    Page 95
LRNSAM                                                                     DRAFT


                                   Appendix G
                                   ________ _

                         Modifier field and mode tables
                         ________ _____ ___ ____ ______





G-1. Modifier parameters
Id    Size Definition          Function
--------------------------------------------------------------------------------
M0    30   COEFF0              coefficient
M1    30   COEFF1              other coefficient
L0    20   TERM_0              running term
L1    20   TERM_1              other running term
MIN   8    A_IN                read address in sum memory
MRM   8    B_IN                other read address
MSUM  7    SUM_MEMORY          write address in
                               sum memory
           ADD_SUM_MEMORY      same as SUM_MEMORY.
           REPLACE_SUM_MEMORY  also has write addr.
                               but result replaces sum mem. value
MMODE 9    MODE                run mode of modifier
                               and scale factor for the multiplies
              A_SCALE          scales COEFF1.
              B_SCALE          scales COEFF0.























                                    Page 96
LRNSAM                   Modifier field and mode tables                    DRAFT


G-2. Modifier procedures
Definition Function
--------------------------------------------------------------------------------
M_INACTIVE inactive.
MIXING      fixed binary point result of COEFF_0*A_IN + COEFF_1*B_IN.

INT_MIXING Integer result of COEFF_0*A_IN + COEFF_1*B_IN.

LATCH      Result ← Term_1. If B_IN*COEFF_0 ≠ 0 then TERM_1←A_IN.

SIGNUM     If A_IN*COEFF0 < B_IN*COEFF1 then result ← -1 else if
           A_IN*COEFF0 = B_IN*COEFF1 then result ← 0 else if
           A_IN*COEFF0 > B_IN*COEFF1 then result ← 1.

ZERO_CROSSING_PULSER
           If (B_IN*COEFF0)*(L1*COEFF1) ≤ 0 then result ← - 1
           else result ← 0. L1 ← B_IN*COEFF0.

MINIMUM    The lesser of A_IN*COEFF0 or B_IN*COEFF1 is returned.

MAXIMUM    The greater of A_IN*COEFF0 or B_IN*COEFF1 is returned.

AMPLITUDE_MODULATION
           Result ← L1*COEFF1.  L1 ← A_IN*((B_IN+1)/2).

FOUR_QUAD_MULTIPLY
           Result ← TERM_1*COEFF1.  TERM_1 ← A_IN*B_IN.

U_NOISE    TERM_1 ← Result ← COEFF0*TERM_1 + TERM_0.

TR_U_NOISE Result ← COEFF0*TERM_1 + TERM_0.
           If B_IN*COEFF1 ≠ 0 then TERM_1←result.

THRESHOLD  If COEFF0*A_IN + TERM_0 < 0 then result ← 0, else
           if ≥ 0 then result ← COEFF1*B_IN.

FILTERING:
           ONE_POLE
           ONE_ZERO
           TWO_0POLES COEFF0 variable
           TWO_1POLES COEFF1 variable
           TWO_0ZEROS COEFF0 variable
           TWO_1ZEROS coeff1 variable






                                    Page 97
LRNSAM                                                                     DRAFT


                                   Appendix H
                                   ________ _

                        Delay unit field and mode tables
                        _____ ____ _____ ___ ____ ______


Fields:

Id     Definition    Function
--------------------------------------------------------------------------------
P      MODE          Delay unit mode
Z      DELAY_LENGTH  Delay line unit length
       SCALE          or table address scale factor
Y      INDEX         Running index of the delay array
X      BASE_ADDRESS  Base address of delay array

Modes:
       D_INACTIVE    dead
       DELAYLINE
       TABLE_LOOKUP
       ROUNDED_TABLE_LOOKUP



H-1. Delay units: timing

Delay units have their own formula: Delay memory cycles available per pass =

(<#_of_processing_ticks> - 6) / 4.                                       Equ. 17

(If  you are  loading delay  memory  while doing  processing, i.e.,  if  you are
changing delay memory arrays from the main computer during execution of samples,
then the number of delay cycles is this number less however many cycles the main
computer may  take to  load the  array.  Each  delay memory  cycle used  to load
arrays in this  fashion will load  one word.  This  method probably will  not be
used much.  The typical usage will be to load the arrays before cranking  up the
box, so that the sound processing is not in competition with computer I/O.)













                                    Page 98
LRNSAM                                                                     DRAFT


                                   Appendix I
                                   ________ _

                                 Reserved Words
                                 ________ _____

a_in                                     dac_write
a_running                                data_read
a_scale                                  data_write
adc                                      decode
add_sum_memory                           delay
all_sum_memory                           delay_length
angle                                    delayline
asymptote                                delays
b_in                                     diagnostic_address
b_running                                dis_ticks
b_scale                                  disk_in
base_address                             disk_out
bias_delay                               dmio
bias_end                                 doinp
bias_generator                           dooutp
bias_modifier                            drio
bias_sum_memory                          dwell
bind                                     dx
bind_field                               dx_load
c_running                                ena_ticks
c_start                                  envelope
c_stop                                   envlsb
c_tick                                   envmask
clear_passes_dwell                       exp_just(x)
coeff0                                   exponent
coeff1                                   flush
coni_a                                   fm
coni_b                                   fn_maximum
coni_d                                   fn_minimum
coniab                                   fnlsb
cono_a                                   fnmask
cono_b                                   four_quad_multiply
control_mode                             freq_just(x)
cos_fm                                   frequency
cosine                                   full_word
d_data(x)                                function
d_inactive                               g_data(x)
d_number(x)                              g_inactive
d_op(x)                                  g_number(x)
d_whole_data(x)                          g_op(x)
dac                                      g_pause




                                    Page 99
LRNSAM                           Reserved Words                            DRAFT






g_wait                                   misc_stop(x)
gen_last_pass_sum_memory                 misc_wait_clear(x)
gen_this_pass_sum_memory                 mixing
generators                               mod_last_pass_sum_memory
get                                      mod_this_pass_sum_memory
give                                     mode
id_delay                                 modifiers
id_generator                             ncosines
id_maximum                               no_asymptote(x)
id_modifier                              no_fm(x)
id_sum_memory                            no_gmode(x)
index                                    no_gsum(x)
inhibit_processing_ticks                 no_in(x)
initialize                               no_l(x)
int_mixing                               no_m(x)
k_clear(x)                               no_mmode(x)
l0_clear(x)                              no_msum(x)
l1_clear(x)                              no_n(x)
latch                                    no_ncosines(x)
left_justified                           no_rm(x)
lexpminus                                no_scale(x)
lexpplus                                 non_optimize
lminusq                                  notwopoles
load_delay                               notwozeroes
lounge                                   o_angle
lplusq                                   o_asymptote
m_data(x)                                o_base
m_inactive                               o_delay
m_just(x)                                o_dmode
m_number(x)                              o_exponent
m_op(x)                                  o_fm
m_select(x)                              o_frequency
max_envelope                             o_gmode
max_oscillator                           o_gsum
maximum                                  o_in
min_envelope                             o_index
min_oscillator                           o_l
minimum                                  o_l_0
misc_data(x)                             o_l_1
misc_op(x)                               o_m
misc_pause_clear(x)                      o_m_0




                                    Page 100
LRNSAM                           Reserved Words                            DRAFT






o_m_1                                    osclsb
o_mmode                                  oscmask
o_msum                                   p_a_scale(x)
o_ncosines                               p_angle(x)
o_rate                                   p_asymptote(x)
o_rm                                     p_b_scale(x)
o_scale                                  p_cause(x)
o_size                                   p_control(x)
o_sweep                                  p_d_mode(x)
one_pole                                 p_d_scale(x)
one_zero                                 p_drb(x)
op_angle                                 p_exponent(x)
op_asymptote                             p_fm(x)
op_base                                  p_frequency(x)
op_delay                                 p_g_envelope(x)
op_dmode                                 p_g_osc_mode(x)
op_exponent                              p_g_run_mode(x)
op_fm                                    p_gmode(x)
op_frequency                             p_gsum(x)
op_gmode                                 p_in(x)
op_gsum                                  p_int(x)
op_in                                    p_l_0(x)
op_index                                 p_l_1(x)
op_l                                     p_l(x)
op_l_0                                   p_m_0(x)
op_l_1                                   p_m_1(x)
op_m                                     p_m_function(x)
op_m_0                                   p_m(x)
op_m_1                                   p_mmode(x)
op_mmode                                 p_mr(x)
op_msum                                  p_msum(x)
op_ncosines                              p_ncosines(x)
op_rate                                  p_pia(x)
op_rm                                    p_rate(x)
op_scale                                 p_re(x)
op_size                                  p_rm(x)
op_sweep                                 p_rtc(x)
op_ticks                                 p_scale(x)
op_timer                                 p_spt(x)
optimize                                 p_sweep(x)
osc_mode                                 packing_mode




                                    Page 101
LRNSAM                           Reserved Words                            DRAFT






pass                                     time_data(x)
pass_clear                               time_op(x)
pass_set                                 tix_processing
pause_clear                              tix_total
permit_processing_ticks                  total_ticks
PIA                                      tr_u_noise
processing_ticks                         ttl_load
pulse_train                              ttla
qflush                                   ttlb
qlens                                    two_0poles
rate                                     two_0zeroes
relative                                 two_1poles
replace_sum_memory                       two_1zeroes
reset                                    u_noise
reset_tick_counter                       unit_generators
right_justified                          update_tick
round_table_lookup                       wait_clear
run_mode                                 zero_crossing_pulser
runlsb
runmask
sawtooth
scale
set_channel
set_field
set_mode
set_output
set_passes
set_procedure
signum
size_buffer
size_commands
square
stop
sum_memory
sum_of_cosines
sweep
table_lookup
term_0
term_1
tick_data(x)
tick_op(x)




                                    Page 102
LRNSAM                                                                     DRAFT


                                   Appendix J
                                   ________ _

                                 Error messages
                                 _____ ________


Bind: Delay port used for sum memory
Bind: Identifier out of range
Bind: Invalid name for identifier type
Bind: Invalid sum memory location
Bind: name out of range
Get: Identifier type out of range
Give: identifier out of range
Set_channel: Zero channel
Set_field: Field index out of range
Set_mode: Mode index out of range
Warning: Extraneous bits on in generator bind
Warning: Extraneous bits on in modifier coefficient bind
Warning: No output channels!































                                    Page 103
LRNSAM                               Index                                 DRAFT


                                   I N D E X

                        (References are to Page numbers)


A_IN                            43, 45    CPU                                 13
A_RUNNING                           23
A_SCALE                             43    DAC                             36, 65
ADC                                 65    DAC path                            14
ADD_SUM_MEMORY                      44    DAC_WRITE                           24
Addressing sum memory               17    data underrun                        8
Amplitude modulation                48    DECODE                              66
AMPLITUDE_MODULATION                48    Definition                      20, 34
ANGLE                       27, 37, 39    Definitions                          2
Arithmetic overflow     23, 24, 46, 48    delay line                          10
array all_sum_memory                70    Delay line example          91, 92, 93
array delays                        70    Delay line mode                     61
array gen_sum_memory                70    delay memory             2, 10, 11, 61
array generators                    70    delay unit                           2
array mod_sum_memory                70    Delay unit field and mode tables    98
array modifiers                     70    Delay Units                     61, 62
array unit_generators               70    Delay units: timing                 98
ASYMPTOTE                   27, 32, 38    DELAYLINE                           61
                                          digital filtering theory            54
B_IN                            43, 45    direct memory access                13
B_RUNNING                           23    disk                                65
B_SCALE                             43    disk bandwidth                       8
band limiting                       31    DMA                             13, 24
band-limited pulse train            30    Duration scaling                    40
bandwidth                           55    DWELL                               15
base address                        66
Basic number theory     76, 77, 78, 79    envelope                        10, 19
BIND                                64    Envelope modes                  34, 35
BIND_FIELD                          68    Envelope processing             32, 33
block structure                     72    Error messages                     103
                                          exiting                             70
C_RUNNING                           24    EXPONENT                    27, 32, 38
Chaining processing elements        16
clear all pause bits                22    Filtering algorithms52, 53, 54, 55,
clear all wait bits                 23                                    56, 57
COEFF0                              43    fixed binary point notation         45
COEFF1                              43    flow of data                13, 14, 15
Coefficient terms                   43    FLUSH                               70
command buffer                      69    FM                          24, 27, 38
Command path                        13    foldover                            31
command underrun                15, 69    formant filter                      55
COS_FM                          30, 37    Four quadrant multiply              48
COSINE                          29, 31    FOUR_QUAD_MULTIPLY                  48

                                    Page 104
LRNSAM                               Index                                 DRAFT







Four-quadrant-multiply              48    Latch                               46
fractional notation                 45    left-shifting                       43
Free mode                           23    LEXMINUS                            35
FREQUENCY                   27, 37, 39    LEXPLUS                             34
frequency response                  54    LMINUSQ                             34
Frequency scaling                   39    LOAD_DELAY                          69
                                          low-pass filter                     54
G_PAUSE                             22    LOWER.DEF                           63
G_WAIT                          23, 24    LOWER.REL                           63
generator                        2, 74    LPLUSQ                              34
Generator example                   80    LSH                                 43
Generator field and mode tables 94, 95
Generator parameters                20    M_INACTIVE                          45
Generator run modes     22, 23, 24, 25    MAG                                 39
Generator tick time                  5    Magic numbers               39, 40, 41
Generators              10, 19, 20, 21    Maximum                             47
GET                                 63    Minimum                             47
GFM                                 38    Mixing                              45
GIVE                                64    MODE                                43
GJ                                  37    modifier                         2, 74
GK                                  37    Modifier example                    82
GL                                  38    Modifier field and mode tables  96, 97
GM                                  37    Modifier parameters                 42
GN                                  37    Modifier procedures                 97
GO                                  36    Modifier scaling                    43
GP                              32, 38    Modifier tick time                   5
GQ                              32, 38    Modifiers           10, 42, 43, 44, 61
GSUM                            21, 38
                                          NCOSINES                            37
high-pass filter                    55    Normalizing                         38
                                          Numbering Processing elements        9
I/O path                            14
INACTIVE                        22, 45    One pole                        52, 54
Initialization          37, 44, 47, 62    One zero                        52, 55
INITIALIZE                          69    oscillator                      10, 19
INT_MIXING                          46    Oscillator modes            29, 30, 31
Integer mixing                      46
Integer notation                    46    pass                  2, 7, 71, 73, 75
Interrupt                           14    pass period                          8
Invoke delay unit                   50    pipelining                      72, 73
INVOKE_DELAY_UNIT                   50    processing degradation               8
                                          processing element                   2
last pass                           16    Processing element arrays           70

                                    Page 105
LRNSAM                               Index                                 DRAFT







processing elements         10, 11, 74    sum memory                   2, 10, 11
Processing speed               7, 8, 9    SUM_MEMORY              24, 33, 38, 44
Processing ticks            2, 3, 4, 8    SUM_OF_COSINES              30, 31, 37
pulse train                         31    SWEEP                           24, 27
PULSE_TRAIN                     30, 31    SWEEP scaling                       40

RATE                    27, 32, 36, 38    table lookup                        69
Read path                           14    table lookup memory                 10
Read terms                          43    Table lookup mode               61, 91
READ_DATA                           24    TABLE_LOOKUP                        62
Real time                         7, 8    TERM_0                          42, 48
refreshing                          27    TERM_1                          42, 46
RELATIVE                            66    this pass                           16
REPLACE_SUM_MEMORY                  44    Threshold                           50
Reserved Words       99, 100, 101, 102    tick                             2, 75
Resonant filter scaling             55    Tick counters                       71
resonant frequency                  55    Tick time requirements            5, 6
resonator                           55    Time division multiplexing      74, 75
Ring modulation                     48    TR_U_NOISE                          49
ROUND_TABLE_LOOKUP                  62    Trigger mode                        24
Running terms                       42    Triggered uniform noise             49
                                          Two poles                       53, 55
SAIL                            20, 63    Two poles COEFF0 variable           53
SAIL examples  80, 81, 82, 83, 84, 85,    Two poles COEFF1 variable           53
                    86, 87, 88, 89, 90    two quadrant multiply               48
sample                            1, 2    Two zeros                           53
sample period                        8    Two zeros COEFF0 variable           54
sampling rate                        1    Two zeros COEFF1 variable           54
SAWTOOTH                        29, 31
SCALE                           37, 46    U_NOISE                             48
Scaling terms                       43    Uniform noise                       48
SET_CHANNEL                         65    Update ticks                2, 3, 4, 8
SET_FIELD                           67    update_tick                         71
SET_MODE                            67
SET_OUTPUT                      24, 65    Write path                          14
SET_PROCEDURE                       65    WRITE_DAC                   36, 37, 65
Signum                              47    WRITE_DATA                      24, 37
Size of command buffer              69    Writing your own procedures         92
source file                         63
Speed of processing                  7    x_RUNNING                           23
SQUARE                          30, 31
Sticky mode                     23, 32    ZERO_CROSSING_PULSER                47
Stream processing                   14    Zero-crossing pulser                47

                                    Page 106